diff --git a/.github/actions/conan-install/action.yml b/.github/actions/conan-install/action.yml index d4e7597e4e3..bf04ed2db0b 100644 --- a/.github/actions/conan-install/action.yml +++ b/.github/actions/conan-install/action.yml @@ -23,5 +23,6 @@ runs: run: | # Run twice to work around OpenSSL not being found. See: https://github.com/overte-org/overte-conan-recipes/issues/1 # `--format=json > build.json` creates a graph of dependencies, which can later be used to check for cache misses. - conan install . -s compiler.cppstd=${{ inputs.cppstd }} -s build_type=${{ inputs.build_type }} -o 'Overte/*:qt_source='${{ inputs.qt_source }} -b missing -pr=${{ inputs.conan_profile }} -of build --format=json > build.json - conan install . -s compiler.cppstd=${{ inputs.cppstd }} -s build_type=${{ inputs.build_type }} -o 'Overte/*:qt_source='${{ inputs.qt_source }} -b missing -pr=${{ inputs.conan_profile }} -of build + # `--build="glib/*"` works around https://github.com/conan-io/conan-center-index/issues/17876 by always building glib from source, instead of using the too outdated binary cache on ConanCenter. + conan install . -s compiler.cppstd=${{ inputs.cppstd }} -s build_type=${{ inputs.build_type }} -o 'Overte/*:qt_source='${{ inputs.qt_source }} -b missing --build="glib/*" -pr=${{ inputs.conan_profile }} -of build --format=json > build.json + conan install . -s compiler.cppstd=${{ inputs.cppstd }} -s build_type=${{ inputs.build_type }} -o 'Overte/*:qt_source='${{ inputs.qt_source }} -b missing --build="glib/*" -pr=${{ inputs.conan_profile }} -of build diff --git a/.github/actions/setup-windows/action.yml b/.github/actions/setup-windows/action.yml index 401a9249494..b93a634a74e 100644 --- a/.github/actions/setup-windows/action.yml +++ b/.github/actions/setup-windows/action.yml @@ -6,7 +6,7 @@ runs: - name: Set environment variables shell: bash run: | - if [[ "${{ matrix.os }}" = "Windows 2022" ]]; then + if [[ "${{ matrix.os }}" = "Windows 2022"* ]]; then echo "CONAN_PROFILE='./tools/conan-profiles/vs-22-release'" >> $GITHUB_ENV else echo "Not sure which Conan profile to use. Exiting." && exit 1 @@ -27,6 +27,7 @@ runs: run: | choco install nsis --allow-downgrade --version=3.06.1 choco install cmake --allow-downgrade --version=3.31.6 # Our dependencies don't support CMake 4.0 yet. + choco install nodejs --version="24.16.0" # Qt6 needs NodeJS for building Qt WebEngine, but cannot find preinstalled NodeJS. Invoke-WebRequest https://build-deps.overte.org/conan/nsis-overte-plugins/NSIS-hifi-plugins-1.0.zip -OutFile NSIS-hifi-plugins.zip Expand-Archive -LiteralPath 'NSIS-hifi-plugins.zip' -DestinationPath 'C:/Program Files (x86)/' diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index f388666932b..0b320551cc2 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -30,49 +30,62 @@ jobs: strategy: matrix: include: - - os: Windows 2022 + - os: Windows 2022 Source # We use a third-party runner hosted by depot.dev to avoid timing out in case Qt has to be built from source. runner: depot-windows-2022-16 # Which GitHub Actions Runner to use. When using `image` this just needs to run the build container. arch: x86_64 build_type: full qt_source: source # Which Qt Conan package to use. `system`, `source`, or `aqt`. rendering_backend: OpenGL # Which OVERTE_RENDERING_BACKEND to build. + - os: Windows 2022 AQt + runner: depot-windows-2022-16 + arch: x86_64 + build_type: full + qt_source: aqt + rendering_backend: OpenGL #- os: macOS-10.15 # build_type: full # qt_source: aqt - - os: Ubuntu 22.04 OpenGL + - os: Ubuntu 22.04 OpenGL Source image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 # Container image used for building. Built from /tools/ci-script/linux-ci/Dockerfile_x - runner: ubuntu-latest + runner: depot-ubuntu-24.04-16 arch: amd64 build_type: full - qt_source: system + qt_source: source rendering_backend: OpenGL - - os: Ubuntu 22.04 Vulkan - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 - runner: ubuntu-latest - arch: amd64 - build_type: full - qt_source: system - rendering_backend: Vulkan + # - os: Ubuntu 22.04 OpenGL system package + # image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 # Container image used for building. Built from /tools/ci-script/linux-ci/Dockerfile_x + # runner: depot-ubuntu-24.04-16 + # arch: amd64 + # build_type: full + # qt_source: system + # rendering_backend: OpenGL + # - os: Ubuntu 22.04 Vulkan + # image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 + # runner: ubuntu-latest + # arch: amd64 + # build_type: full + # qt_source: system + # rendering_backend: Vulkan # Android builds are currently failing #- os: ubuntu-18.04 # build_type: android # apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 python3-github python3-distro # Do not change the names of self-hosted runners without knowing what you are doing, as they correspond to labels that have to be set on the runner. - - os: Ubuntu 22.04 OpenGL - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 - runner: ubuntu-24.04-arm - arch: aarch64 - build_type: full - qt_source: system - rendering_backend: OpenGL - - os: Ubuntu 22.04 Vulkan - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 - runner: ubuntu-24.04-arm - arch: aarch64 - build_type: full - qt_source: system - rendering_backend: Vulkan + # - os: Ubuntu 22.04 OpenGL + # image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 + # runner: ubuntu-24.04-arm + # arch: aarch64 + # build_type: full + # qt_source: source + # rendering_backend: OpenGL + # - os: Ubuntu 22.04 Vulkan + # image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 + # runner: ubuntu-24.04-arm + # arch: aarch64 + # build_type: full + # qt_source: system + # rendering_backend: Vulkan fail-fast: false runs-on: ${{matrix.runner}} container: ${{matrix.image}} diff --git a/BUILD_WIN.md b/BUILD_WIN.md index e4d00d2f522..07d3dab7390 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -76,10 +76,13 @@ To create this variable: *Make sure that you copy the contents of your old `.conan2` folder over, or that you add the remote from step 2 again.* -## Step 6. (Optional) Node.JS and NPM +## Step 6. Installing Node.JS and NPM Install the latest LTS version of [Node.JS and NPM](). This is required to build the server-console and jsdoc, and for JavaScript console autocompletion. +All of these components are optional, however, Node.JS is also required for building Qt WebEngine. + +There are no proper Windows installers for Node.JS and NPM, so install it from [Chocolatey](https://community.chocolatey.org/). ## Step 7. Running CMake to Generate Build Files @@ -188,4 +191,4 @@ To fix this, install "C++ vXX.XX (...) ATL (...)" replacing XX.XX with the first ### `warning : cannot resolve item 'api-ms-win-(...)-l1-1-0.dll'` You may be able to ignore this warning. My assumption is that the MSVC version used is incompatible with the currently installed Windows SDK, -and Overte will just use the newer SDK during runtime. \ No newline at end of file +and Overte will just use the newer SDK during runtime. diff --git a/CMakeLists.txt b/CMakeLists.txt index 968f8a0826b..b6e538541db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,7 @@ if (ANDROID) add_definitions(-DANDROID_APP_INTERFACE) endif() else () - set(PLATFORM_QT_COMPONENTS WebEngine Xml) + set(PLATFORM_QT_COMPONENTS WebEngineCore Xml) add_definitions(-DHAVE_EXT_clip_cull_distance) endif() @@ -160,7 +160,7 @@ else() endif() foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) - list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") + list(APPEND PLATFORM_QT_LIBRARIES "Qt6::${PLATFORM_QT_COMPONENT}") endforeach() message(STATUS "Use optimized IK: " ${OVERTE_USE_OPTIMIZED_IK}) diff --git a/android/apps/framePlayer/src/main/cpp/qml/main.qml b/android/apps/framePlayer/src/main/cpp/qml/main.qml index 34c4507f9de..a8fd27d907e 100644 --- a/android/apps/framePlayer/src/main/cpp/qml/main.qml +++ b/android/apps/framePlayer/src/main/cpp/qml/main.qml @@ -1,5 +1,5 @@ import QtQuick 2.2 -import QtQuick.Dialogs 1.1 +import QtQuick.Dialogs import Qt.labs.folderlistmodel 2.11 Item { diff --git a/android/apps/interface/src/main/cpp/native.cpp b/android/apps/interface/src/main/cpp/native.cpp index 42118d89db0..b8ade2f8452 100644 --- a/android/apps/interface/src/main/cpp/native.cpp +++ b/android/apps/interface/src/main/cpp/native.cpp @@ -407,7 +407,7 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_retrieveAccessToken(JN QMetaObject::invokeMethod(accountManager.data(), "requestAccessTokenWithAuthCode", Q_ARG(const QString&, authCode), Q_ARG(const QString&, clientId), - Q_ARG(const QString&, clientSecret), Q_ARG(const QString&, redirectUri)); + Q_GENERIC_ARG(const QString&, clientSecret), Q_GENERIC_ARG(const QString&, redirectUri)); } diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 39833b9cb95..7b5dd1bd2f0 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -753,7 +753,7 @@ void Agent::processAgentAvatarAudio() { if (_numAvatarSoundSentBytes == (int)audioData->getNumBytes()) { // we're done with this sound object - so set our pointer back to NULL // and our sent bytes back to zero - _avatarSound.clear(); + _avatarSound.reset(); _numAvatarSoundSentBytes = 0; _flushEncoder = true; diff --git a/assignment-client/src/AgentScriptingInterface.h b/assignment-client/src/AgentScriptingInterface.h index 1146c4006ba..277ed3d773f 100644 --- a/assignment-client/src/AgentScriptingInterface.h +++ b/assignment-client/src/AgentScriptingInterface.h @@ -101,7 +101,7 @@ public slots: * }, 1000); * }()); */ - void playAvatarSound(SharedSoundPointer avatarSound) const { _agent->playAvatarSound(avatarSound); } + void playAvatarSound(std::shared_ptr avatarSound) const { _agent->playAvatarSound(avatarSound); } private: Agent* _agent; diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 901586ce60e..67fdcbae3bb 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -295,7 +295,8 @@ void AssignmentClientMonitor::spawnChildClient() { void AssignmentClientMonitor::checkSpares() { auto nodeList = DependencyManager::get(); - QUuid aSpareId = ""; + // QT6TODO: it was originally composed for empty QString, is it null UUID? + QUuid aSpareId {}; unsigned int spareCount = 0; unsigned int totalCount = 0; diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 731687b6374..d3da1d759cf 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -434,7 +435,7 @@ void AssetServer::completeSetup() { // Check the asset directory to output some information about what we have auto files = _filesDirectory.entryList(QDir::Files); - QRegExp hashFileRegex { AssetUtils::ASSET_HASH_REGEX_STRING }; + QRegularExpression hashFileRegex { AssetUtils::ASSET_HASH_REGEX_STRING }; auto hashedFiles = files.filter(hashFileRegex); qCInfo(asset_server) << "There are" << hashedFiles.size() << "asset files in the asset directory."; diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index a1db33fbc87..112753b84df 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -64,14 +64,14 @@ void ScriptableAvatar::stopAnimation() { QMetaObject::invokeMethod(this, "stopAnimation"); return; } - _animation.clear(); + _animation.reset(); } AnimationDetails ScriptableAvatar::getAnimationDetails() { if (QThread::currentThread() != thread()) { AnimationDetails result; BLOCKING_INVOKE_METHOD(this, "getAnimationDetails", - Q_RETURN_ARG(AnimationDetails, result)); + Q_GENERIC_RETURN_ARG(AnimationDetails, result)); return result; } return _animationDetails; @@ -222,7 +222,7 @@ void ScriptableAvatar::update(float deltatime) { } } else { - _animation.clear(); + _animation.reset(); } } } diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h index 2c256c8516c..58044bf3d95 100644 --- a/assignment-client/src/octree/OctreeHeadlessViewer.h +++ b/assignment-client/src/octree/OctreeHeadlessViewer.h @@ -43,14 +43,14 @@ public slots: * @function EntityViewer.setPosition * @param {Vec3} position - The position of the view frustum. */ - void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); } + void setPosition(const glm::vec<3,float,glm::packed_highp>& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); } /*@jsdoc * Sets the orientation of the view frustum. * @function EntityViewer.setOrientation * @param {Quat} orientation - The orientation of the view frustum. */ - void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); } + void setOrientation(const glm::qua& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); } /*@jsdoc * Sets the radius of the center "keyhole" in the view frustum. @@ -99,14 +99,14 @@ public slots: * @function EntityViewer.getPosition * @returns {Vec3} The position of the view frustum. */ - const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); } + const glm::vec<3,float,glm::packed_highp>& getPosition() const { return _viewFrustum.getPosition(); } /*@jsdoc * Gets the orientation of the view frustum. * @function EntityViewer.getOrientation * @returns {Quat} The orientation of the view frustum. */ - const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); } + const glm::qua& getOrientation() const { return _viewFrustum.getOrientation(); } // getters for LOD and PPS diff --git a/cmake/macros/MemoryDebugger.cmake b/cmake/macros/MemoryDebugger.cmake index 7181586e0b6..ca470e1166f 100644 --- a/cmake/macros/MemoryDebugger.cmake +++ b/cmake/macros/MemoryDebugger.cmake @@ -41,9 +41,9 @@ if (OVERTE_MEMORY_DEBUGGING) # usr/bin/ld: ../../libraries/audio/libaudio.so: undefined reference to `FIR_1x4_AVX512(float*, float*, float*, float*, float*, float (*) [64], int)' # The '-DSTACK_PROTECTOR' argument below disables the usage of this function in the code. This should be fine as it only works on the latest Intel hardware, # and is an optimization that should make no functional difference. - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -U_FORTIFY_SOURCE -DSTACK_PROTECTOR -fstack-protector-strong -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak") - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -U_FORTIFY_SOURCE -DSTACK_PROTECTOR -fstack-protector-strong -fno-omit-frame-pointer -fsanitize-recover=address") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -fsanitize-recover=address") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -fsanitize-recover=address") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # https://docs.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-160 # Supported experimentally starting from VS2019 v16.4, and officially from v16.9. diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 65d8e200618..e4207190602 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -14,7 +14,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) if (WIN32) set(PLUGIN_PATH "plugins") - get_target_property(Qt_Core_Location Qt5::Core LOCATION) + get_target_property(Qt_Core_Location Qt6::Core LOCATION) get_filename_component(QT_BIN_DIR ${Qt_Core_Location} DIRECTORY) find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_BIN_DIR}) @@ -23,7 +23,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) endif () # add a post-build command to call windeployqt to copy Qt plugins - set(CMD "${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} --no-compiler-runtime --no-opengl-sw --no-angle -no-system-d3d-compiler") + set(CMD "${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} --no-compiler-runtime --no-opengl-sw -no-system-d3d-compiler") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/windeploy-${TARGET_NAME}.bat" "${CMD} %*") add_custom_command( diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index aa37b2a3a87..cbc090c9f76 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -64,13 +64,13 @@ macro(SETUP_HIFI_LIBRARY) endif () set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN}) - list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core) + list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core Core5Compat) # find these Qt modules and link them to our own target - find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) + find_package(Qt6 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) - target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) + target_link_libraries(${TARGET_NAME} Qt6::${QT_MODULE}) endforeach() # Don't make scribed shaders, generated entity files, generated pipelines, or QT resource files cumulative diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index a4da4a3a056..4b5dc0eb130 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -28,10 +28,10 @@ macro(SETUP_HIFI_PROJECT) target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN}) - list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core) + list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core Core5Compat) # find these Qt modules and link them to our own target - find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) + find_package(Qt6 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings @@ -41,7 +41,7 @@ macro(SETUP_HIFI_PROJECT) endif() foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) - target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) + target_link_libraries(${TARGET_NAME} Qt6::${QT_MODULE}) endforeach() target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/cmake/macros/SetupHifiTestCase.cmake b/cmake/macros/SetupHifiTestCase.cmake index 017a0222f55..4df4829dc4a 100644 --- a/cmake/macros/SetupHifiTestCase.cmake +++ b/cmake/macros/SetupHifiTestCase.cmake @@ -108,10 +108,10 @@ macro(SETUP_HIFI_TESTCASE) list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core Test) # find these Qt modules and link them to our own target - find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) + find_package(Qt6 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) - target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) + target_link_libraries(${TARGET_NAME} Qt6::${QT_MODULE}) endforeach() target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/cmake/macros/TargetQuazip.cmake b/cmake/macros/TargetQuazip.cmake index bf495536475..a37a9197ec8 100644 --- a/cmake/macros/TargetQuazip.cmake +++ b/cmake/macros/TargetQuazip.cmake @@ -1,18 +1,26 @@ -# +# # Copyright 2015 High Fidelity, Inc. +# Copyright 2025 Overte e.V. # Created by Leonardo Murillo on 2015/11/20 # # Distributed under the Apache License, Version 2.0. # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# +# macro(TARGET_QUAZIP) + # System QuaZip sets `IMPORTED_GLOBAL` for itself when finding it using find_package(). + # Running find_package() a second time shouldn't do anything, since the package was already found, + # yet it fails with: + # `Attempt to promote imported target "QuaZip::QuaZip" to global scope (by setting IMPORTED_GLOBAL) which is not built in this directory.` + # We avoid this error by guarding against running find_package() multiple times. if(OVERTE_USE_SYSTEM_LIBS) find_package(PkgConfig REQUIRED) - pkg_check_modules(QuaZip REQUIRED quazip1-qt5) + pkg_check_modules(QuaZip REQUIRED quazip1-qt6) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${QuaZip_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${QuaZip_LINK_LIBRARIES}) else() - find_package(QuaZip-Qt5 REQUIRED) - target_link_libraries(${TARGET_NAME} QuaZip::QuaZip) + if(NOT TARGET QuaZip::QuaZip) # if target doesn't exist. + find_package(QuaZip-Qt6 REQUIRED) + endif() + target_link_libraries(${TARGET_NAME} QuaZip::QuaZip) endif() endmacro() diff --git a/cmake/modules/FindQt5LinguistToolsMacros.cmake b/cmake/modules/FindQt5LinguistToolsMacros.cmake index bd9d55cb160..90e1bbb6dcc 100644 --- a/cmake/modules/FindQt5LinguistToolsMacros.cmake +++ b/cmake/modules/FindQt5LinguistToolsMacros.cmake @@ -30,7 +30,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= -function(QT5_CREATE_TRANSLATION_CUSTOM _qm_files) +function(QT6_CREATE_TRANSLATION_CUSTOM _qm_files) set(options) set(oneValueArgs) set(multiValueArgs OPTIONS) @@ -76,7 +76,7 @@ function(QT5_CREATE_TRANSLATION_CUSTOM _qm_files) get_source_file_property(_qm_output_location ${_ts_file} OUTPUT_LOCATION) add_custom_command( OUTPUT ${_tmpts_file} - COMMAND ${Qt5_LUPDATE_EXECUTABLE} + COMMAND ${Qt6_LUPDATE_EXECUTABLE} ARGS ${_lupdate_options} "@${_ts_lst_file}" -ts ${_ts_file} COMMAND ${CMAKE_COMMAND} -E copy ${_ts_file} ${_tmpts_file} DEPENDS ${_my_sources} ${_ts_lst_file} VERBATIM) @@ -84,12 +84,12 @@ function(QT5_CREATE_TRANSLATION_CUSTOM _qm_files) set_property(SOURCE ${_tmpts_file} PROPERTY OUTPUT_LOCATION ${_qm_output_location}) endif() endforeach() - qt5_add_translation(${_qm_files} ${_my_temptsfiles}) + qt6_add_translation(${_qm_files} ${_my_temptsfiles}) set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) endfunction() -function(QT5_ADD_TRANSLATION _qm_files) +function(QT6_ADD_TRANSLATION _qm_files) foreach(_current_FILE ${ARGN}) get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE) get_filename_component(qm ${_abs_FILE} NAME_WE) @@ -102,7 +102,7 @@ function(QT5_ADD_TRANSLATION _qm_files) endif() add_custom_command(OUTPUT ${qm} - COMMAND ${Qt5_LRELEASE_EXECUTABLE} + COMMAND ${Qt6_LRELEASE_EXECUTABLE} ARGS ${_abs_FILE} -qm ${qm} DEPENDS ${_abs_FILE} VERBATIM ) diff --git a/conanfile.py b/conanfile.py index 582e9a8b357..9993d18505e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,6 +24,7 @@ class Overte(ConanFile): "openssl*:shared": "True", "qt*:shared": "True", "qt*:gui": "True", + "qt*:qt5compat": "True", # Required by Quazip 1.4 and probably us "qt*:qtdeclarative": "True", "qt*:qtimageformats": "True", # WebP texture support "qt*:qtlocation": "True", @@ -34,6 +35,7 @@ class Overte(ConanFile): "qt*:qtsvg": "True", "qt*:qtwebchannel": "True", "qt*:qtwebengine": "True", + "qt*:qtshadertools": "True", # For Qt WebEngineWidgets "qt*:qtwebsockets": "True", "qt*:qtwebview": "True", "qt*:qtx11extras": "True", # Required by gpu-frame-player on Linux @@ -72,7 +74,7 @@ def requirements(self): self.requires("openexr/3.1.9") self.requires("openvr/2.2.3@overte/stable") self.requires("openxr/1.1.46@overte/stable") - self.requires("opus/1.5.2") + self.requires("opus/1.5.2", force=True) # The Qt6 source package depends on an older opus version than we do. self.requires("quazip/1.4") self.requires("scribe/2019.02@overte/stable") self.requires("sdl/2.32.10") @@ -88,13 +90,13 @@ def requirements(self): openssl = "openssl/1.1.1q" if self.options.qt_source == "system": - self.requires("qt/5.15.2@overte/system", force=True) + self.requires("qt/6.x@overte/system", force=True) if self.settings.os == "Linux": openssl = "openssl/system@anotherfoxguy/stable" elif self.options.qt_source == "aqt": - self.requires("qt/5.15.2@overte/aqt", force=True) + self.requires("qt/6.10.3@overte/aqt#c692bf111ff0a41e7b82ef12dec20219", force=True) else: - self.requires("qt/5.15.18-2026.01.04@overte/stable#4fc772a2dbcd84731eb6ff9904e6e358", force=True) + self.requires("qt/6.11.1@overte/experimental#f3b43b7235810a2e064268e976386ca0", force=True) if self.settings.os == "Windows": self.requires("neuron/12.2@overte/prebuild") @@ -103,6 +105,8 @@ def requirements(self): self.requires(openssl, force=True) + self.requires("glib/2.85.3", override=True) # Fix version conflict resulting from Qt and GStreamer. + def generate(self): tc = CMakeToolchain(self) @@ -134,8 +138,8 @@ def generate(self): if self.settings.compiler == "gcc": self.output.status("GCC compiler detected, setting default flags.") tc.cache_variables.update({ - "CMAKE_CXX_FLAGS_DEBUG_INIT": "-Og -ggdb3", - "CMAKE_C_FLAGS_DEBUG_INIT": "-Og -ggdb3", + "CMAKE_CXX_FLAGS_DEBUG_INIT": "-O0 -ggdb3", + "CMAKE_C_FLAGS_DEBUG_INIT": "-O0 -ggdb3", "CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT": "-O2 -DNDEBUG -ggdb2", "CMAKE_C_FLAGS_RELWITHDEBINFO_INIT": "-O2 -DNDEBUG -ggdb2", "CMAKE_CXX_FLAGS_RELEASE_INIT": "-O3 -DNDEBUG", @@ -144,8 +148,8 @@ def generate(self): elif self.settings.compiler == "clang": self.output.status("Clang compiler detected, setting default flags.") tc.cache_variables.update({ - "CMAKE_CXX_FLAGS_DEBUG_INIT": "-Og -g", - "CMAKE_C_FLAGS_DEBUG_INIT": "-Og -g", + "CMAKE_CXX_FLAGS_DEBUG_INIT": "-O0 -g", + "CMAKE_C_FLAGS_DEBUG_INIT": "-O0 -g", "CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT": "-O2 -DNDEBUG -g", "CMAKE_C_FLAGS_RELWITHDEBINFO_INIT": "-O2 -DNDEBUG -g", "CMAKE_CXX_FLAGS_RELEASE_INIT": "-O3 -DNDEBUG", diff --git a/domain-server/src/ContentSettingsBackupHandler.cpp b/domain-server/src/ContentSettingsBackupHandler.cpp index 90f95349347..dfc75f7e58b 100644 --- a/domain-server/src/ContentSettingsBackupHandler.cpp +++ b/domain-server/src/ContentSettingsBackupHandler.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic pop @@ -102,7 +103,7 @@ std::pair ContentSettingsBackupHandler::recoverBackup(const QStri zipFile.close(); if (zipFile.getZipError() != UNZ_OK) { - QString errorStr("Failed to unzip " + CONTENT_SETTINGS_BACKUP_FILENAME + ": " + zipFile.getZipError()); + QString errorStr("Failed to unzip " + CONTENT_SETTINGS_BACKUP_FILENAME + ": " + QString::number(zipFile.getZipError())); qCritical() << errorStr; return { false, errorStr }; } diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index b22b001144f..0d7f86e0341 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -81,7 +82,7 @@ void DomainContentBackupManager::parseBackupRules(const QVariantList& backupRule int count = map["maxBackupVersions"].toInt(); auto name = map["Name"].toString(); auto format = name.toLower(); - QRegExp matchDisallowedCharacters { "[^a-zA-Z0-9\\-_]+" }; + QRegularExpression matchDisallowedCharacters { "[^a-zA-Z0-9\\-_]+" }; format.replace(matchDisallowedCharacters, "_"); qCDebug(domain_server) << " Name:" << name; @@ -313,8 +314,8 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, }; if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "recoverFromBackup", Q_ARG(MiniPromise::Promise, promise), - Q_ARG(const QString&, backupName), Q_ARG(const QString&, username)); + QMetaObject::invokeMethod(this, "recoverFromBackup", Q_GENERIC_ARG(MiniPromise::Promise, promise), + Q_GENERIC_ARG(const QString&, backupName), Q_GENERIC_ARG(const QString&, username)); return; } diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 3844db12f57..432e8a57495 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -340,7 +341,7 @@ void DomainGatekeeper::updateNodePermissions() { // authentication and verifiedUsername is only set once they user's key has been confirmed. QString verifiedUsername = node->getPermissions().getVerifiedUserName(); QString verifiedDomainUserName = node->getPermissions().getVerifiedDomainUserName(); - NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0)); + NodePermissions userPerms(NodePermissionsKey(verifiedUsername, QUuid())); if (node->getPermissions().isAssignment) { // this node is an assignment-client @@ -469,7 +470,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect auto limitedNodeList = DependencyManager::get(); // start with empty permissions - NodePermissions userPerms(NodePermissionsKey(username, 0)); + NodePermissions userPerms(NodePermissionsKey(username, QUuid())); userPerms.setAll(false); // check if this user is on our local machine - if this is true set permissions to those for a "localhost" connection @@ -1094,11 +1095,11 @@ void DomainGatekeeper::getIsGroupMemberJSONCallback(QNetworkReply* requestReply) QJsonObject groups = data["groups"].toObject(); QString username = data["username"].toString(); _server->_settingsManager.clearGroupMemberships(username); - foreach (auto groupID, groups.keys()) { + for (const auto &groupID: groups.keys()) { QJsonObject group = groups[groupID].toObject(); QJsonObject rank = group["rank"].toObject(); QUuid rankID = QUuid(rank["id"].toString()); - _server->_settingsManager.recordGroupMembership(username, groupID, rankID); + _server->_settingsManager.recordGroupMembership(username, QUuid::fromString(groupID), rankID); } } else { qDebug() << "getIsGroupMember api call returned:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2f13fe30587..9b71a18027e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -255,7 +255,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : ch.setEnabled(_settingsManager.valueOrDefaultValueForKeyPath(CRASH_REPORTER).toBool()); qRegisterMetaType("DomainServerWebSessionData"); - qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); + //qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); // make sure we hear about newly connected nodes from our gatekeeper connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode); @@ -699,7 +699,7 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply* requestReply) { // store the new token to the account info auto accountManager = DependencyManager::get(); - accountManager->setTemporaryDomain(id, key); + accountManager->setTemporaryDomain(QUuid::fromString(id), key); // change our domain ID immediately DependencyManager::get()->setSessionUUID(QUuid { id }); @@ -831,7 +831,7 @@ void DomainServer::setupNodeListAndAssignments() { } else { QVariant idValueVariant = _settingsManager.valueForKeyPath(METAVERSE_DOMAIN_ID_KEY_PATH); if (idValueVariant.isValid()) { - nodeList->setSessionUUID(idValueVariant.toString()); + nodeList->setSessionUUID(QUuid::fromString(idValueVariant.toString())); isMetaverseDomain = true; // if we have an ID, we'll assume we're a metaverse domain } else { nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID @@ -1002,7 +1002,7 @@ bool DomainServer::resetAccountManagerAccessToken() { if (accessToken.isEmpty()) { QVariant accessTokenVariant = _settingsManager.valueForKeyPath(ACCESS_TOKEN_KEY_PATH); - if (accessTokenVariant.canConvert(QMetaType::QString)) { + if (accessTokenVariant.canConvert(QMetaType(QMetaType::QString))) { accessToken = accessTokenVariant.toString(); } else { qWarning() << "No access token is present. Some operations that use the directory services API will fail."; @@ -1207,7 +1207,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q int configCounter = 0; foreach(const QVariant& configVariant, configList) { - if (configVariant.canConvert(QMetaType::QVariantMap)) { + if (configVariant.canConvert(QMetaType(QMetaType::QVariantMap))) { QVariantMap configMap = configVariant.toMap(); // check the config string for a pool @@ -2849,11 +2849,12 @@ std::pair DomainServer::isAuthenticatedRequest(HTTPConnection* c if (_oauthEnable) { QString cookieString = connection->requestHeader(HTTP_COOKIE_HEADER_KEY); - QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING); + QRegularExpression cookieUUIDRegex(COOKIE_UUID_REGEX_STRING); QUuid cookieUUID; - if (cookieString.indexOf(cookieUUIDRegex) != -1) { - cookieUUID = cookieUUIDRegex.cap(1); + auto matches = cookieUUIDRegex.match(cookieString); + if (matches.hasMatch()) { + cookieUUID = QUuid::fromString(matches.captured(1)); } if (_settingsManager.valueForKeyPath(BASIC_AUTH_USERNAME_KEY_PATH).isValid()) { @@ -2950,7 +2951,7 @@ std::pair DomainServer::isAuthenticatedRequest(HTTPConnection* c QString settingsPassword = settingsPasswordVariant.isValid() ? settingsPasswordVariant.toString() : ""; QString hexHeaderPassword = headerPassword.isEmpty() ? - "" : QString(QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex()); + QByteArray("") : QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) { qCInfo(domain_server_auth) << httpPeerAddress << "- Basic:" << headerUsername << "-" @@ -3025,7 +3026,7 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR // setup expiry for cookie to 1 month from today QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); - QString cookieString = HIFI_SESSION_COOKIE_KEY + "=" + uuidStringWithoutCurlyBraces(cookieUUID.toString()); + QString cookieString = HIFI_SESSION_COOKIE_KEY + "=" + uuidStringWithoutCurlyBraces(cookieUUID); cookieString += "; expires=" + cookieExpiry.toString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; cookieString += "; domain=" + _hostname + "; path=/"; diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index 80e5fea56a2..a4ed43215c0 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -28,7 +28,13 @@ DomainServerNodeData::DomainServerNodeData() { } void DomainServerNodeData::updateJSONStats(const QByteArray& statsByteArray) { - _statsJSONObject = overrideValuesIfNeeded(QCborValue::fromCbor(statsByteArray).toJsonValue().toObject()); + QCborParserError cborError; + auto cbor = QCborValue::fromCbor(statsByteArray, &cborError); + if (cborError.error != QCborError::NoError) { + qWarning() << "DomainServerNodeData::updateJSONStats: corrupted cbor: " << QString(statsByteArray.toHex()); + return; + } + _statsJSONObject = overrideValuesIfNeeded(cbor.toJsonValue().toObject()); } QJsonObject DomainServerNodeData::overrideValuesIfNeeded(const QJsonObject& newStats) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index fe13c29df21..7eec5c1c56d 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -256,7 +256,7 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena QVariant* allowedUsers = _configMap.valueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH); if (allowedUsers - && allowedUsers->canConvert(QMetaType::QVariantList) + && allowedUsers->canConvert(QMetaType(QMetaType::QVariantList)) && reinterpret_cast(allowedUsers)->size() > 0) { qDebug() << "Forcing security.restricted_access to TRUE since there was an" @@ -277,7 +277,7 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena // this was prior to change of poorly named entitiesFileName to entitiesFilePath QVariant* persistFileNameVariant = _configMap.valueForKeyPath(ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); - if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { + if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType(QMetaType::QString))) { QString persistFileName = persistFileNameVariant->toString(); qDebug() << "Migrating persistFilename to persistFilePath for entity-server settings"; @@ -290,7 +290,7 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena // remove the old setting QVariant* entityServerVariant = _configMap.valueForKeyPath(ENTITY_SERVER_SETTINGS_KEY); - if (entityServerVariant && entityServerVariant->canConvert(QMetaType::QVariantMap)) { + if (entityServerVariant && entityServerVariant->canConvert(QMetaType(QMetaType::QVariantMap))) { QVariantMap entityServerMap = entityServerVariant->toMap(); entityServerMap.remove(ENTITY_FILE_NAME_KEY); @@ -307,7 +307,7 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena QVariant* passwordVariant = _configMap.valueForKeyPath(BASIC_AUTH_PASSWORD_KEY_PATH); - if (passwordVariant && passwordVariant->canConvert(QMetaType::QString)) { + if (passwordVariant && passwordVariant->canConvert(QMetaType(QMetaType::QString))) { QString plaintextPassword = passwordVariant->toString(); qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings."; @@ -343,12 +343,12 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena foreach (QString allowedUser, allowedUsers) { // even if isRestrictedAccess is false, we have to add explicit rows for these users. - _agentPermissions[NodePermissionsKey(allowedUser, 0)].reset(new NodePermissions(allowedUser)); - _agentPermissions[NodePermissionsKey(allowedUser, 0)]->set(NodePermissions::Permission::canConnectToDomain); + _agentPermissions[NodePermissionsKey(allowedUser, QUuid())].reset(new NodePermissions(allowedUser)); + _agentPermissions[NodePermissionsKey(allowedUser, QUuid())]->set(NodePermissions::Permission::canConnectToDomain); } foreach (QString allowedEditor, allowedEditors) { - NodePermissionsKey editorKey(allowedEditor, 0); + NodePermissionsKey editorKey(allowedEditor, QUuid()); if (!_agentPermissions.contains(editorKey)) { _agentPermissions[editorKey].reset(new NodePermissions(allowedEditor)); if (isRestrictedAccess) { @@ -613,13 +613,13 @@ void DomainServerSettingsManager::packPermissionsForMap(QString mapName, // find (or create) the "security" section of the settings map QVariant* security = _configMap.valueForKeyPath("security", true); - if (!security->canConvert(QMetaType::QVariantMap)) { + if (!security->canConvert(QMetaType(QMetaType::QVariantMap))) { (*security) = QVariantMap(); } // find (or create) whichever subsection of "security" we are packing QVariant* permissions = _configMap.valueForKeyPath(keyPath, true); - if (!permissions->canConvert(QMetaType::QVariantList)) { + if (!permissions->canConvert(QMetaType(QMetaType::QVariantList))) { (*permissions) = QVariantList(); } @@ -707,7 +707,7 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key return false; } - if (!permissions.canConvert(QMetaType::QVariantList)) { + if (!permissions.canConvert(QMetaType(QMetaType::QVariantList))) { qDebug() << "Failed to extract permissions for key path" << keyPath << "from settings."; } @@ -969,7 +969,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointergetMachineFingerprint().toString(), 0); + NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), QUuid()); // check if there were already permissions for the fingerprint bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint()); @@ -1115,7 +1115,7 @@ NodePermissions DomainServerSettingsManager::getStandardPermissionsForName(const } NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString& name) const { - NodePermissionsKey nameKey = NodePermissionsKey(name, 0); + NodePermissionsKey nameKey = NodePermissionsKey(name, QUuid()); if (_agentPermissions.contains(nameKey)) { return *(_agentPermissions[nameKey].get()); } @@ -1125,7 +1125,7 @@ NodePermissions DomainServerSettingsManager::getPermissionsForName(const QString } NodePermissions DomainServerSettingsManager::getPermissionsForIP(const QHostAddress& address) const { - NodePermissionsKey ipKey = NodePermissionsKey(address.toString(), 0); + NodePermissionsKey ipKey = NodePermissionsKey(address.toString(), QUuid()); if (_ipPermissions.contains(ipKey)) { return *(_ipPermissions[ipKey].get()); } @@ -1135,7 +1135,7 @@ NodePermissions DomainServerSettingsManager::getPermissionsForIP(const QHostAddr } NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString& macAddress) const { - NodePermissionsKey macKey = NodePermissionsKey(macAddress, 0); + NodePermissionsKey macKey = NodePermissionsKey(macAddress, QUuid()); if (_macPermissions.contains(macKey)) { return *(_macPermissions[macKey].get()); } @@ -1145,7 +1145,7 @@ NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString& } NodePermissions DomainServerSettingsManager::getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const { - NodePermissionsKey fingerprintKey = NodePermissionsKey(machineFingerprint.toString(), 0); + NodePermissionsKey fingerprintKey = NodePermissionsKey(machineFingerprint.toString(), QUuid()); if (_machineFingerprintPermissions.contains(fingerprintKey)) { return *(_machineFingerprintPermissions[fingerprintKey].get()); } @@ -1672,7 +1672,7 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV QVariant& possibleMap = settingMap[key]; - if (!possibleMap.canConvert(QMetaType::QVariantMap)) { + if (!possibleMap.canConvert(QMetaType(QMetaType::QVariantMap))) { // if this isn't a map then we need to make it one, otherwise we're about to crash qDebug() << "Value at" << key << "was not the expected QVariantMap while updating DS settings" << "- removing existing value and making it a QVariantMap"; @@ -1890,8 +1890,8 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ // Compare two members of a permissions list bool permissionVariantLessThan(const QVariant &v1, const QVariant &v2) { - if (!v1.canConvert(QMetaType::QVariantMap) || - !v2.canConvert(QMetaType::QVariantMap)) { + if (!v1.canConvert(QMetaType(QMetaType::QVariantMap)) || + !v2.canConvert(QMetaType(QMetaType::QVariantMap))) { return v1.toString() < v2.toString(); } QVariantMap m1 = v1.toMap(); @@ -1916,22 +1916,22 @@ void DomainServerSettingsManager::sortPermissions() { // sort the permission-names QVariant* standardPermissions = _configMap.valueForKeyPath(AGENT_STANDARD_PERMISSIONS_KEYPATH); - if (standardPermissions && standardPermissions->canConvert(QMetaType::QVariantList)) { + if (standardPermissions && standardPermissions->canConvert(QMetaType(QMetaType::QVariantList))) { QList* standardPermissionsList = reinterpret_cast(standardPermissions); std::sort((*standardPermissionsList).begin(), (*standardPermissionsList).end(), permissionVariantLessThan); } QVariant* permissions = _configMap.valueForKeyPath(AGENT_PERMISSIONS_KEYPATH); - if (permissions && permissions->canConvert(QMetaType::QVariantList)) { + if (permissions && permissions->canConvert(QMetaType(QMetaType::QVariantList))) { QList* permissionsList = reinterpret_cast(permissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } QVariant* groupPermissions = _configMap.valueForKeyPath(GROUP_PERMISSIONS_KEYPATH); - if (groupPermissions && groupPermissions->canConvert(QMetaType::QVariantList)) { + if (groupPermissions && groupPermissions->canConvert(QMetaType(QMetaType::QVariantList))) { QList* permissionsList = reinterpret_cast(groupPermissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } QVariant* forbiddenPermissions = _configMap.valueForKeyPath(GROUP_FORBIDDENS_KEYPATH); - if (forbiddenPermissions && forbiddenPermissions->canConvert(QMetaType::QVariantList)) { + if (forbiddenPermissions && forbiddenPermissions->canConvert(QMetaType(QMetaType::QVariantList))) { QList* permissionsList = reinterpret_cast(forbiddenPermissions); std::sort((*permissionsList).begin(), (*permissionsList).end(), permissionVariantLessThan); } @@ -2173,11 +2173,11 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply* re if (jsonObject["status"].toString() == "success") { QJsonObject groups = jsonObject["data"].toObject()["groups"].toObject(); - foreach (auto groupID, groups.keys()) { + for (auto const &groupID: groups.keys()) { QJsonObject group = groups[groupID].toObject(); QJsonArray ranks = group["ranks"].toArray(); - QHash& ranksForGroup = _groupRanks[groupID]; + QHash& ranksForGroup = _groupRanks[QUuid::fromString(groupID)]; QHash idsFromThisUpdate; for (int rankIndex = 0; rankIndex < ranks.size(); rankIndex++) { @@ -2199,7 +2199,7 @@ void DomainServerSettingsManager::apiGetGroupRanksJSONCallback(QNetworkReply* re } // clean up any that went away - foreach (QUuid rankID, ranksForGroup.keys()) { + for (const QUuid &rankID: ranksForGroup.keys()) { if (!idsFromThisUpdate.contains(rankID)) { ranksForGroup.remove(rankID); } diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 3c7959291cb..c443662761b 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -95,25 +95,25 @@ class DomainServerSettingsManager : public QObject { bool containsKeyPath(const QString& keyPath) { return valueForKeyPath(keyPath).isValid(); } // these give access to anonymous/localhost/logged-in settings from the domain-server settings page - bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name, 0); } + bool haveStandardPermissionsForName(const QString& name) const { return _standardAgentPermissions.contains(name, QUuid()); } NodePermissions getStandardPermissionsForName(const NodePermissionsKey& name) const; // these give access to permissions for specific user-names from the domain-server settings page - bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name, 0); } + bool havePermissionsForName(const QString& name) const { return _agentPermissions.contains(name, QUuid()); } NodePermissions getPermissionsForName(const QString& name) const; NodePermissions getPermissionsForName(const NodePermissionsKey& key) const { return getPermissionsForName(key.first); } QStringList getAllNames() const; // these give access to permissions for specific IPs from the domain-server settings page - bool hasPermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), 0); } + bool hasPermissionsForIP(const QHostAddress& address) const { return _ipPermissions.contains(address.toString(), QUuid()); } NodePermissions getPermissionsForIP(const QHostAddress& address) const; // these give access to permissions for specific MACs from the domain-server settings page - bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); } + bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, QUuid()); } NodePermissions getPermissionsForMAC(const QString& macAddress) const; // these give access to permissions for specific machine fingerprints from the domain-server settings page - bool hasPermissionsForMachineFingerprint(const QUuid& machineFingerprint) { return _machineFingerprintPermissions.contains(machineFingerprint.toString(), 0); } + bool hasPermissionsForMachineFingerprint(const QUuid& machineFingerprint) { return _machineFingerprintPermissions.contains(machineFingerprint.toString(), QUuid()); } NodePermissions getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const; // these give access to permissions for specific groups from the domain-server settings page diff --git a/domain-server/src/EntitiesBackupHandler.cpp b/domain-server/src/EntitiesBackupHandler.cpp index 526bf30a5c6..5346353da30 100644 --- a/domain-server/src/EntitiesBackupHandler.cpp +++ b/domain-server/src/EntitiesBackupHandler.cpp @@ -74,7 +74,7 @@ std::pair EntitiesBackupHandler::recoverBackup(const QString& bac zipFile.close(); if (zipFile.getZipError() != UNZ_OK) { - QString errorStr("Failed to unzip " + ENTITIES_BACKUP_FILENAME + ": " + zipFile.getZipError()); + QString errorStr("Failed to unzip " + ENTITIES_BACKUP_FILENAME + ": " + QString::number(zipFile.getZipError())); qCritical() << errorStr; return { false, errorStr }; } diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp index d7d813e3df3..c6fa8134291 100644 --- a/domain-server/src/NodeConnectionData.cpp +++ b/domain-server/src/NodeConnectionData.cpp @@ -23,7 +23,7 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c // Read out the protocol version signature from the connect message char* rawBytes; - uint length; + qsizetype length; dataStream.readBytes(rawBytes, length); newHeader.protocolVersion = QByteArray(rawBytes, length); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 92ac16c68bd..62422d20201 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -25,7 +25,7 @@ endfunction() set(CUSTOM_INTERFACE_QRC_PATHS "") find_package( - Qt5 COMPONENTS + Qt6 COMPONENTS Gui Widgets Multimedia Network Qml Quick Svg WebEngineCore WebEngineWidgets ${PLATFORM_QT_COMPONENTS} WebChannel WebSockets @@ -43,9 +43,9 @@ generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources if (ANDROID) # on Android, don't compress the rcc binary - qt5_add_binary_resources(resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}" OPTIONS -no-compress) + qt6_add_binary_resources(resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}" OPTIONS -no-compress) else () - qt5_add_binary_resources(resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}") + qt6_add_binary_resources(resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}") endif() if (OVERTE_BUILD_TOOLS AND JSDOC_ENABLED) @@ -105,7 +105,7 @@ endif () file (GLOB_RECURSE QT_UI_FILES ui/*.ui) source_group("UI Files" FILES ${QT_UI_FILES}) # have qt5 wrap them and generate the appropriate header files -qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") +qt6_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") # add them to the interface source files set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") @@ -113,7 +113,7 @@ set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") # translation disabled until we strip out the line numbers # set(QM ${TARGET_NAME}_en.qm) # set(TS ${TARGET_NAME}_en.ts) -# qt5_create_translation_custom(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) +# qt6_create_translation_custom(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) # setup the android parameters that will help us produce an APK if (ANDROID) @@ -295,11 +295,13 @@ if (ANDROID) target_link_libraries(${TARGET_NAME} ${ANDROID_LOG_LIB}) endif () +find_package(Qt6GuiPrivate) + target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets - Qt5::Qml Qt5::Quick Qt5::Svg - Qt5::WebChannel Qt5::WebEngineCore Qt5::WebEngineWidgets + Qt6::Gui Qt6::GuiPrivate Qt6::Network Qt6::Multimedia Qt6::Widgets + Qt6::Qml Qt6::Quick Qt6::Svg + Qt6::WebChannel Qt6::WebEngineCore Qt6::WebEngineWidgets ${PLATFORM_QT_LIBRARIES} ) diff --git a/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml index 97d83f43049..6fd28a47032 100644 --- a/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml +++ b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml @@ -1,6 +1,6 @@ import QtQuick 2.4 -import QtQuick.Dialogs 1.1 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import "../../qml/dialogs" diff --git a/interface/resources/fonts/Roboto-Bold.ttf b/interface/resources/fonts/Roboto-Bold.ttf new file mode 100644 index 00000000000..4658f9a67b1 Binary files /dev/null and b/interface/resources/fonts/Roboto-Bold.ttf differ diff --git a/interface/resources/fonts/Roboto-BoldItalic.ttf b/interface/resources/fonts/Roboto-BoldItalic.ttf new file mode 100644 index 00000000000..2ee0765ce17 Binary files /dev/null and b/interface/resources/fonts/Roboto-BoldItalic.ttf differ diff --git a/interface/resources/fonts/Roboto-Italic.ttf b/interface/resources/fonts/Roboto-Italic.ttf new file mode 100644 index 00000000000..c3abaefb284 Binary files /dev/null and b/interface/resources/fonts/Roboto-Italic.ttf differ diff --git a/interface/resources/fonts/Roboto-Regular.ttf b/interface/resources/fonts/Roboto-Regular.ttf new file mode 100644 index 00000000000..7e3bb2f8ce7 Binary files /dev/null and b/interface/resources/fonts/Roboto-Regular.ttf differ diff --git a/interface/resources/fonts/Roboto.license b/interface/resources/fonts/Roboto.license new file mode 100644 index 00000000000..a417551aa4a --- /dev/null +++ b/interface/resources/fonts/Roboto.license @@ -0,0 +1,93 @@ +Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/interface/resources/fonts/RobotoMono-Bold.ttf b/interface/resources/fonts/RobotoMono-Bold.ttf new file mode 100644 index 00000000000..bef439f3b9a Binary files /dev/null and b/interface/resources/fonts/RobotoMono-Bold.ttf differ diff --git a/interface/resources/fonts/RobotoMono-BoldItalic.ttf b/interface/resources/fonts/RobotoMono-BoldItalic.ttf new file mode 100644 index 00000000000..642dd0589f0 Binary files /dev/null and b/interface/resources/fonts/RobotoMono-BoldItalic.ttf differ diff --git a/interface/resources/fonts/RobotoMono-Italic.ttf b/interface/resources/fonts/RobotoMono-Italic.ttf new file mode 100644 index 00000000000..781eff8c0a7 Binary files /dev/null and b/interface/resources/fonts/RobotoMono-Italic.ttf differ diff --git a/interface/resources/fonts/RobotoMono-Regular.ttf b/interface/resources/fonts/RobotoMono-Regular.ttf new file mode 100644 index 00000000000..3806bfb1101 Binary files /dev/null and b/interface/resources/fonts/RobotoMono-Regular.ttf differ diff --git a/interface/resources/icons/tablet-icons/chat-a.svg b/interface/resources/icons/tablet-icons/chat-a.svg new file mode 100644 index 00000000000..e3793aaf022 --- /dev/null +++ b/interface/resources/icons/tablet-icons/chat-a.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/icons/tablet-icons/chat-i.svg b/interface/resources/icons/tablet-icons/chat-i.svg new file mode 100644 index 00000000000..c5d26819952 --- /dev/null +++ b/interface/resources/icons/tablet-icons/chat-i.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/+android_interface/Web3DSurfaceAndroid.qml b/interface/resources/qml/+android_interface/Web3DSurfaceAndroid.qml index 6a9f76bc739..135c966c63f 100644 --- a/interface/resources/qml/+android_interface/Web3DSurfaceAndroid.qml +++ b/interface/resources/qml/+android_interface/Web3DSurfaceAndroid.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml index 291859c3ec6..7ce14553969 100644 --- a/interface/resources/qml/+webengine/BrowserWebView.qml +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -1,8 +1,8 @@ -import QtQuick 2.5 -import QtWebChannel 1.0 -import QtWebEngine 1.5 +import QtQuick +import QtWebChannel +import QtWebEngine -import controlsUit 1.0 +import controlsUit WebView { id: webview @@ -10,23 +10,20 @@ WebView { profile: FileTypeProfile; property var parentRoot: null - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.Deferred - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] + userScripts.collection: [ + // Create a global EventBridge object for raiseAndLowerKeyboard. + { + sourceCode: eventBridgeJavaScriptToInject, + injectionPoint: WebEngineScript.Deferred, + worldId: WebEngineScript.MainWorld, + }, + // Detect when may want to raise and lower keyboard. + { + injectionPoint: WebEngineScript.Deferred, + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js", + worldId: WebEngineScript.MainWorld, + } + ] onLoadingChanged: { if (loadRequest.status === WebEngineView.LoadSucceededStatus) { diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml index 84ab61ad28b..441267975a3 100644 --- a/interface/resources/qml/+webengine/QmlWebWindowView.qml +++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml @@ -1,9 +1,9 @@ -import QtQuick 2.5 -import QtWebEngine 1.1 -import QtWebChannel 1.0 +import QtQuick +import QtWebEngine +import QtWebChannel -import controlsUit 1.0 as Controls -import stylesUit 1.0 +import controlsUit as Controls +import stylesUit Controls.WebView { id: webview url: "about:blank" @@ -14,30 +14,28 @@ Controls.WebView { property string userScriptUrl: "" // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: webview.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + userScripts.collection: [ { + name: "createGlobalEventBridge", + id: createGlobalEventBridge, + sourceCode: eventBridgeJavaScriptToInject, + injectionPoint: WebEngineScript.DocumentCreation, + worldId: WebEngineScript.MainWorld + }, + { + name: "raiseAndLowerKeyboardScript", + id: raiseAndLowerKeyboard, + injectionPoint: WebEngineScript.Deferred, + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js", + worldId: WebEngineScript.MainWorld + }, + { + name: "userWebEngineScript", + id: userScript, + sourceUrl: webview.userScriptUrl, + injectionPoint: WebEngineScript.DocumentReady, // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } ] function onWebEventReceived(event) { if (typeof event === "string" && event.slice(0, 17) === "CLARA.IO DOWNLOAD") { diff --git a/interface/resources/qml/+webengine/TabletBrowser.qml b/interface/resources/qml/+webengine/TabletBrowser.qml index 49b87e51dd7..367b0872c56 100644 --- a/interface/resources/qml/+webengine/TabletBrowser.qml +++ b/interface/resources/qml/+webengine/TabletBrowser.qml @@ -1,11 +1,11 @@ -import QtQuick 2.5 -import QtWebChannel 1.0 -import QtWebEngine 1.5 +import QtQuick +import QtWebChannel +import QtWebEngine import "controls" -import controlsUit 1.0 as HifiControls +import controlsUit as HifiControls import "styles" as HifiStyles -import stylesUit 1.0 +import stylesUit import "windows" Item { diff --git a/interface/resources/qml/AudioScopeUI.qml b/interface/resources/qml/AudioScopeUI.qml index 75a71a32c5f..01f2e023baa 100644 --- a/interface/resources/qml/AudioScopeUI.qml +++ b/interface/resources/qml/AudioScopeUI.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit diff --git a/interface/resources/qml/AvatarInputsBar.qml b/interface/resources/qml/AvatarInputsBar.qml index 9230111cb64..3997d28340d 100644 --- a/interface/resources/qml/AvatarInputsBar.qml +++ b/interface/resources/qml/AvatarInputsBar.qml @@ -8,7 +8,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "./hifi/audio" as HifiAudio diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 2fefb6bff47..8727d558f9e 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -163,7 +163,7 @@ ScrollingWindow { anchors.leftMargin: 0 anchors.verticalCenter: parent.verticalCenter focus: true - colorScheme: hifi.colorSchemes.dark + //colorScheme: hifi.colorSchemes.dark placeholderText: "Enter URL" inputMethodHints: Qt.ImhUrlCharactersOnly Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") @@ -229,8 +229,8 @@ ScrollingWindow { Button { id:allow text: "Allow" - color: hifi.buttons.blue - colorScheme: root.colorScheme + //color: hifi.buttons.blue + //colorScheme: root.colorScheme width: 120 enabled: true onClicked: root.allowPermissions(); @@ -240,8 +240,8 @@ ScrollingWindow { Button { id:block text: "Block" - color: hifi.buttons.red - colorScheme: root.colorScheme + //color: hifi.buttons.red + //colorScheme: root.colorScheme width: 120 enabled: true onClicked: root.hidePermissionsBar(); diff --git a/interface/resources/qml/BubbleIcon.qml b/interface/resources/qml/BubbleIcon.qml index b5a7ddb2d86..0ec3a927357 100644 --- a/interface/resources/qml/BubbleIcon.qml +++ b/interface/resources/qml/BubbleIcon.qml @@ -8,7 +8,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "./hifi/audio" as HifiAudio diff --git a/interface/resources/qml/ConnectionFailureDialog.qml b/interface/resources/qml/ConnectionFailureDialog.qml index cb6c9db4576..a3186fa642b 100644 --- a/interface/resources/qml/ConnectionFailureDialog.qml +++ b/interface/resources/qml/ConnectionFailureDialog.qml @@ -1,4 +1,4 @@ -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import "dialogs" diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml index 4ea45041c3d..7a7cfa382f0 100644 --- a/interface/resources/qml/CurrentAPI.qml +++ b/interface/resources/qml/CurrentAPI.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/interface/resources/qml/InteractiveWindow.qml b/interface/resources/qml/InteractiveWindow.qml index a8b27762d6c..740717ca973 100644 --- a/interface/resources/qml/InteractiveWindow.qml +++ b/interface/resources/qml/InteractiveWindow.qml @@ -76,10 +76,11 @@ Windows.Window { } function updateInteractiveWindowPositionForMode() { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { x = interactiveWindowPosition.x; y = interactiveWindowPosition.y; - } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { + } else if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow) { if (interactiveWindowPosition.x === 0 && interactiveWindowPosition.y === 0) { // default position for native window in center of main application window nativeWindow.x = Math.floor(Window.x + (Window.innerWidth / 2) - (interactiveWindowSize.width / 2)); @@ -97,28 +98,31 @@ Windows.Window { contentHolder.width = interactiveWindowSize.width; contentHolder.height = interactiveWindowSize.height; - if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow) { nativeWindow.width = interactiveWindowSize.width; nativeWindow.height = interactiveWindowSize.height; } } function updateContentParent() { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { contentHolder.parent = root; - } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { + } else if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow) { contentHolder.parent = nativeWindow.contentItem; } } function setupPresentationMode() { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { if (nativeWindow) { nativeWindow.setVisible(false); } updateInteractiveWindowPositionForMode(); shown = interactiveWindowVisible; - } else if (presentationMode === Desktop.PresentationMode.NATIVE) { + } else if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1) { shown = false; if (nativeWindow) { updateInteractiveWindowPositionForMode(); @@ -175,25 +179,29 @@ Windows.Window { nativeWindow.height = interactiveWindowSize.height; nativeWindow.xChanged.connect(function() { - if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow.visible) { interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y); } }); nativeWindow.yChanged.connect(function() { - if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow.visible) { interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y); } }); nativeWindow.widthChanged.connect(function() { - if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow.visible) { interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height); } }); nativeWindow.heightChanged.connect(function() { - if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow.visible) { interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height); } }); @@ -223,9 +231,10 @@ Windows.Window { } function raiseWindow() { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { raise(); - } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { + } else if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow) { nativeWindow.raise(); } } @@ -271,9 +280,10 @@ Windows.Window { } onInteractiveWindowVisibleChanged: { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { shown = interactiveWindowVisible; - } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { + } else if (presentationMode === /*Desktop.PresentationMode.NATIVE*/ 1 && nativeWindow) { if (!nativeWindow.visible && interactiveWindowVisible) { nativeWindow.showNormal(); } else { @@ -289,25 +299,29 @@ Windows.Window { } onXChanged: { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { interactiveWindowPosition = Qt.point(x, interactiveWindowPosition.y); } } onYChanged: { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, y); } } onWidthChanged: { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { interactiveWindowSize = Qt.size(width, interactiveWindowSize.height); } } onHeightChanged: { - if (presentationMode === Desktop.PresentationMode.VIRTUAL) { + // QT6TODO: Desktop isn't being passed to QML? + if (presentationMode === /*Desktop.PresentationMode.VIRTUAL*/ 0) { interactiveWindowSize = Qt.size(interactiveWindowSize.width, height); } } diff --git a/interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml index 8570273bde7..39852afeccc 100644 --- a/interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/+android_interface/LinkAccountBody.qml @@ -10,8 +10,8 @@ import Hifi 1.0 import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +import QtQuick.Controls 2.3 +////import QtQuick.Controls.Styles as OriginalStyles import controlsUit 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml b/interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml index f2329c065c5..9b1bc80a5fd 100644 --- a/interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/+android_interface/SignUpBody.qml @@ -10,8 +10,8 @@ import Hifi 1.0 import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +import QtQuick.Controls 2.3 +////import QtQuick.Controls.Styles as OriginalStyles import controlsUit 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index b8744251323..0ae8eda186d 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +////import QtQuick.Controls.Styles as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 25aa7504ee3..550210451cc 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +import QtQuick.Controls 2.3 +////import QtQuick.Controls.Styles as OriginalStyles import "." as LoginDialog diff --git a/interface/resources/qml/LoginDialog/LoggingInBody.qml b/interface/resources/qml/LoginDialog/LoggingInBody.qml index 1c8250cfa8e..d8cfd618d66 100644 --- a/interface/resources/qml/LoginDialog/LoggingInBody.qml +++ b/interface/resources/qml/LoginDialog/LoggingInBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +import QtQuick.Controls 2.3 +////import QtQuick.Controls.Styles as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit diff --git a/interface/resources/qml/LoginDialog/LoginDialogLightbox.qml b/interface/resources/qml/LoginDialog/LoginDialogLightbox.qml index 8995ecd2613..e49cc8a27f4 100644 --- a/interface/resources/qml/LoginDialog/LoginDialogLightbox.qml +++ b/interface/resources/qml/LoginDialog/LoginDialogLightbox.qml @@ -13,7 +13,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index b1335232110..b3ca2451a01 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles +import QtQuick.Controls 2.3 +////import QtQuick.Controls.Styles as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 86f60bc067d..5911c3ff860 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit diff --git a/interface/resources/qml/OverlayWindowTest.qml b/interface/resources/qml/OverlayWindowTest.qml index 9c1b993ba84..16692e4083c 100644 --- a/interface/resources/qml/OverlayWindowTest.qml +++ b/interface/resources/qml/OverlayWindowTest.qml @@ -1,5 +1,5 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 Rectangle { width: 100 diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 4aaefb5cce9..27f6202665c 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -1,8 +1,8 @@ import Hifi 1.0 import QtQuick 2.3 -import QtQuick.Controls 1.3 -import QtQuick.Controls.Styles 1.3 -import QtGraphicalEffects 1.0 +import QtQuick.Controls +//import QtQuick.Controls.Styles +import Qt5Compat.GraphicalEffects import controlsUit 1.0 import "styles" as HifiStyles diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 3380eabeac1..fb845debfda 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -17,7 +17,7 @@ Item { id: root anchors.fill: parent property string url: "" - property string scriptUrl: null + property string scriptUrl: "" property bool useBackground: true property string userAgent: "" @@ -83,7 +83,7 @@ Item { if (root.webViewLoaded) { loader.item.url = "about:blank" } - loader.setSource(undefined); + loader.setSource(""); } if (url.match(/\.qml$/)) { @@ -93,7 +93,7 @@ Item { root.webViewLoaded = true; loader.setSource("./controls/WebView.qml", { url: url, - scriptUrl: scriptUrl, + //scriptUrl: scriptUrl, // Qt6 TODO useBackground: useBackground, userAgent: userAgent }); diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index 315b6023d32..5db8d417054 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -1,11 +1,11 @@ -import QtQuick 2.7 -import QtWebEngine 1.5 -import QtWebChannel 1.0 +import QtQuick +import QtWebEngine +import QtWebChannel -import QtQuick.Controls 2.2 +import QtQuick.Controls -import stylesUit 1.0 as StylesUIt -import controlsUit 1.0 as ControlsUit +import stylesUit as StylesUIt +import controlsUit as ControlsUit Item { id: flick @@ -28,7 +28,7 @@ Item { property string urlTag: "noDownload=false"; signal newViewRequestedCallback(var request) - signal loadingChangedCallback(var loadRequest) + signal loadingChangedCallback(var loadingInfo) width: parent.width @@ -116,33 +116,56 @@ Item { settings.touchIconsEnabled: true settings.allowRunningInsecureContent: true - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: flick.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + // QT6TODO causes a crash + // Even empty collection causes a crash + // userScripts.collection: [] + //userScripts.collection: [ + // creates a global EventBridge object. + /*{ + sourceCode: eventBridgeJavaScriptToInject, + injectionPoint: WebEngineScript.DocumentCreation, + worldId: WebEngineScript.MainWorld, + }, + + // detects when to raise and lower virtual keyboard + { + injectionPoint: WebEngineScript.Deferred, + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js", + worldId: WebEngineScript.MainWorld, + }, + + // User script. + { + sourceUrl: flick.userScriptUrl, + injectionPoint: WebEngineScript.DocumentReady, // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld, + },*/ + //] Component.onCompleted: { + // QT6TODO: previous way of doing it crashes and this one doesn't? unless it crashes later + userScripts.collection = [ + // creates a global EventBridge object. + { + sourceCode: eventBridgeJavaScriptToInject, + injectionPoint: WebEngineScript.DocumentCreation, + worldId: WebEngineScript.MainWorld, + }, + + // detects when to raise and lower virtual keyboard + { + injectionPoint: WebEngineScript.Deferred, + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js", + worldId: WebEngineScript.MainWorld, + }, + + // User script. + { + sourceUrl: flick.userScriptUrl, + injectionPoint: WebEngineScript.DocumentReady, // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld, + }, + ] webChannel.registerObject("eventBridge", eventBridge); webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); @@ -176,9 +199,10 @@ Item { request.accepted = true; } - onNewViewRequested: { + // Qt6 TODO + /*onNewViewRequested: { newViewRequestedCallback(request) - } + }*/ // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns @@ -186,15 +210,18 @@ Item { // when QT fixes this. property bool safeLoading: false property bool loadingLatched: false - property var loadingRequest: null - onLoadingChanged: { - webViewCore.loadingRequest = loadRequest; + property var loadInfo: null + // QT6TODO: useBackground was missing here in Qt5, but I cannot find it anywhere in Qt documentation, so I just defined it here. + property bool useBackground: true + + function onLoadingChanged(loadingInfo) { + webViewCore.loadInfo = loadingInfo; webViewCore.safeLoading = webViewCore.loading && !loadingLatched; webViewCore.loadingLatched |= webViewCore.loading; } onSafeLoadingChanged: { - flick.onLoadingChanged(webViewCore.loadingRequest) - loadingChangedCallback(webViewCore.loadingRequest) + flick.onLoadingChanged(webViewCore.loadInfo) + loadingChangedCallback(webViewCore.loadInfo) } } diff --git a/interface/resources/qml/controls/ButtonAwesome.qml b/interface/resources/qml/controls/ButtonAwesome.qml index 12f2a6b40d4..e772ce544d9 100644 --- a/interface/resources/qml/controls/ButtonAwesome.qml +++ b/interface/resources/qml/controls/ButtonAwesome.qml @@ -1,6 +1,6 @@ import QtQuick 2.3 -import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 as OriginalStyles +import QtQuick.Controls as Original +////import QtQuick.Controls.Styles as OriginalStyles import "." import "../styles" @@ -11,7 +11,8 @@ Original.Button { property real size: 32 SystemPalette { id: palette; colorGroup: SystemPalette.Active } SystemPalette { id: disabledPalette; colorGroup: SystemPalette.Disabled } - style: OriginalStyles.ButtonStyle { + // QT6TODO + /*style: OriginalStyles.ButtonStyle { label: FontAwesome { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -48,7 +49,7 @@ Original.Button { onStopped: if (control.activeFocus) { start(); } } } - } + }*/ Keys.onEnterPressed: root.clicked(); Keys.onReturnPressed: root.clicked(); } diff --git a/interface/resources/qml/controls/CheckBox.qml b/interface/resources/qml/controls/CheckBox.qml index 91586299a72..f7a6e1c1353 100644 --- a/interface/resources/qml/controls/CheckBox.qml +++ b/interface/resources/qml/controls/CheckBox.qml @@ -1,5 +1,5 @@ -import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 +import QtQuick.Controls as Original +//import QtQuick.Controls.Styles import "../styles" import "." Original.CheckBox { diff --git a/interface/resources/qml/controls/ComboBox.qml b/interface/resources/qml/controls/ComboBox.qml index 960ae8127aa..50244af7320 100644 --- a/interface/resources/qml/controls/ComboBox.qml +++ b/interface/resources/qml/controls/ComboBox.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import "." as VrControls diff --git a/interface/resources/qml/controls/Player.qml b/interface/resources/qml/controls/Player.qml index 8af0b1527dc..35ce91528a3 100644 --- a/interface/resources/qml/controls/Player.qml +++ b/interface/resources/qml/controls/Player.qml @@ -12,7 +12,7 @@ import QtQuick 2.4 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 -import QtQuick.Controls.Styles 1.2 +//import QtQuick.Controls.Styles import "../styles" Item { diff --git a/interface/resources/qml/controls/RadioButton.qml b/interface/resources/qml/controls/RadioButton.qml index 6bd9c860d2f..dcbae3491bb 100644 --- a/interface/resources/qml/controls/RadioButton.qml +++ b/interface/resources/qml/controls/RadioButton.qml @@ -1,5 +1,5 @@ import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 +//import QtQuick.Controls.Styles import "../styles" import "." Original.RadioButton { diff --git a/interface/resources/qml/controls/Slider.qml b/interface/resources/qml/controls/Slider.qml index ace20f1e682..3c53bbe62d9 100644 --- a/interface/resources/qml/controls/Slider.qml +++ b/interface/resources/qml/controls/Slider.qml @@ -1,5 +1,5 @@ import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 +//import QtQuick.Controls.Styles import "../styles" import "." diff --git a/interface/resources/qml/controls/SpinBox.qml b/interface/resources/qml/controls/SpinBox.qml index 1acba57409e..c317ca96288 100644 --- a/interface/resources/qml/controls/SpinBox.qml +++ b/interface/resources/qml/controls/SpinBox.qml @@ -1,6 +1,6 @@ import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 +//import QtQuick.Controls.Styles import "../styles" import "." diff --git a/interface/resources/qml/controls/TextField.qml b/interface/resources/qml/controls/TextField.qml index df536a1de53..c4900b069ed 100644 --- a/interface/resources/qml/controls/TextField.qml +++ b/interface/resources/qml/controls/TextField.qml @@ -1,7 +1,8 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles TextField { - style: TextFieldStyle { renderType: Text.QtRendering } + //QT6TODO + //style: TextFieldStyle { renderType: Text.QtRendering } } diff --git a/interface/resources/qml/controls/WrappedMenu.qml b/interface/resources/qml/controls/WrappedMenu.qml new file mode 100644 index 00000000000..43fc2d438e3 --- /dev/null +++ b/interface/resources/qml/controls/WrappedMenu.qml @@ -0,0 +1,13 @@ +import QtQuick.Controls + +Menu { + id: wrappedMenu + objectName: "wrappedMenu" + + function addMenuWrap(menu) { + return addMenu(menu); + } + function addItemWrap(item) { + addItem(item); + } +} \ No newline at end of file diff --git a/interface/resources/qml/controlsUit/AttachmentsTable.qml b/interface/resources/qml/controlsUit/AttachmentsTable.qml index a2677962daa..c20bea08180 100644 --- a/interface/resources/qml/controlsUit/AttachmentsTable.qml +++ b/interface/resources/qml/controlsUit/AttachmentsTable.qml @@ -9,8 +9,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.XmlListModel 2.0 import "../stylesUit" diff --git a/interface/resources/qml/controlsUit/ContentSection.qml b/interface/resources/qml/controlsUit/ContentSection.qml index 262c29220fd..a45f45c2956 100644 --- a/interface/resources/qml/controlsUit/ContentSection.qml +++ b/interface/resources/qml/controlsUit/ContentSection.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "../stylesUit" diff --git a/interface/resources/qml/controlsUit/FilterBar.qml b/interface/resources/qml/controlsUit/FilterBar.qml index 0892018913c..e57e7dd3b49 100644 --- a/interface/resources/qml/controlsUit/FilterBar.qml +++ b/interface/resources/qml/controlsUit/FilterBar.qml @@ -10,7 +10,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "../stylesUit" import "." as HifiControls diff --git a/interface/resources/qml/controlsUit/Keyboard.qml b/interface/resources/qml/controlsUit/Keyboard.qml index a55523f34ac..f2842f488eb 100644 --- a/interface/resources/qml/controlsUit/Keyboard.qml +++ b/interface/resources/qml/controlsUit/Keyboard.qml @@ -9,7 +9,7 @@ // import QtQuick 2.7 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." Rectangle { diff --git a/interface/resources/qml/controlsUit/Table.qml b/interface/resources/qml/controlsUit/Table.qml index ab743610460..680a2c42310 100644 --- a/interface/resources/qml/controlsUit/Table.qml +++ b/interface/resources/qml/controlsUit/Table.qml @@ -8,10 +8,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Controls 2.3 as QQC2 +import QtQuick +import QtQuick.Controls +//import QtQuick.Controls.Styles import "../stylesUit" @@ -30,12 +29,14 @@ TableView { model: ListModel { } Component.onCompleted: { - if (flickableItem !== null && flickableItem !== undefined) { + // QT6TODO: ReferenceError: flickableItem is not defined + /*if (flickableItem !== null && flickableItem !== undefined) { tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar - } + }*/ } - QQC2.ScrollBar { + // QT6TODO: this errors out with: QML ScrollBar: Cannot anchor to an item that isn't a parent or sibling. + ScrollBar { id: scrollbar parent: tableView.flickableItem policy: QQC2.ScrollBar.AsNeeded @@ -68,8 +69,10 @@ TableView { } } - headerVisible: false - headerDelegate: Rectangle { + // QT6TODO: not available on Qt6 + //headerVisible: false + // QT6TODO: I don't know how to port this + /*headerDelegate: Rectangle { height: hifi.dimensions.tableHeaderHeight color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark @@ -130,10 +133,11 @@ TableView { } color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight } - } + }*/ // Use rectangle to draw border with rounded corners. - frameVisible: false + // QT6TODO: does not exist in Qt 6? + //frameVisible: false Rectangle { color: "#00000000" anchors { fill: parent; margins: -2 } @@ -142,19 +146,28 @@ TableView { } anchors.margins: 2 // Shrink TableView to lie within border. - backgroundVisible: true + // QT6TODO: this doesn't exist in Qt 6? + //backgroundVisible: true - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + // QT6TODO: these don't exist in Qt 6? + //horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + //verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff - style: TableViewStyle { + // QT6TODO: how to port this + /*style: TableViewStyle { // Needed in order for rows to keep displaying rows after end of table entries. backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 - } + }*/ + + // QT6TODO: I'm not sure if this is correct + // was originally rowDelegate + delegate: Rectangle { + // QT6TODO: I added these, but I'm not sure if these are needed + required property bool current + required property bool selected - rowDelegate: Rectangle { height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight color: styleData.selected ? hifi.colors.primaryHighlight diff --git a/interface/resources/qml/controlsUit/TabletContentSection.qml b/interface/resources/qml/controlsUit/TabletContentSection.qml index dccaf31bbec..c349baa0265 100644 --- a/interface/resources/qml/controlsUit/TabletContentSection.qml +++ b/interface/resources/qml/controlsUit/TabletContentSection.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "../stylesUit" diff --git a/interface/resources/qml/controlsUit/TextField.qml b/interface/resources/qml/controlsUit/TextField.qml index 86707395e35..6577bd9c293 100644 --- a/interface/resources/qml/controlsUit/TextField.qml +++ b/interface/resources/qml/controlsUit/TextField.qml @@ -9,8 +9,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import "../stylesUit" import "." as HifiControls @@ -44,7 +44,7 @@ TextField { y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0 // workaround for https://bugreports.qt.io/browse/QTBUG-49297 - Keys.onPressed: { + Keys.onPressed: event => { switch (event.key) { case Qt.Key_Return: case Qt.Key_Enter: @@ -57,6 +57,8 @@ TextField { } } + // QT6TODO + /* style: TextFieldStyle { id: style; textColor: { @@ -159,7 +161,7 @@ TextField { padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding renderType: textField.styleRenderType - } + }*/ HifiControls.Label { id: textFieldLabel diff --git a/interface/resources/qml/controlsUit/Tree.qml b/interface/resources/qml/controlsUit/Tree.qml index f2c49095b12..930e3a09998 100644 --- a/interface/resources/qml/controlsUit/Tree.qml +++ b/interface/resources/qml/controlsUit/Tree.qml @@ -8,11 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQml.Models 2.2 -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Controls 2.2 as QQC2 +import QtQml.Models +import QtQuick +import QtQuick.Controls +//import QtQuick.Controls.Styles +import QtQuick.Controls as QQC2 import "../stylesUit" diff --git a/interface/resources/qml/controlsUit/WebView.qml b/interface/resources/qml/controlsUit/WebView.qml index 2895f36944d..b3bff4e8b36 100644 --- a/interface/resources/qml/controlsUit/WebView.qml +++ b/interface/resources/qml/controlsUit/WebView.qml @@ -12,10 +12,11 @@ import QtQuick 2.5 import "." BaseWebView { - onNewViewRequested: { + // Qt6 TODO + /*onNewViewRequested: { // Load dialog via OffscreenUi so that JavaScript EventBridge is available. var browser = OffscreenUi.load("Browser.qml"); request.openIn(browser.webView); browser.webView.forceActiveFocus(); - } + }*/ } diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 971fb064148..0a04f76863b 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -8,12 +8,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls 2.3 as QQC2 +import QtQuick +import QtQuick.Controls import "../dialogs" import "../js/Utils.js" as Utils +import "../controls" as OverteControls + +import "../overte/compat" as OverteCompat // This is our primary 'desktop' object to which all VR dialogs and windows are childed. FocusScope { @@ -51,16 +53,21 @@ FocusScope { property bool desktopRoot: true // The VR version of the primary menu - property var rootMenu: Menu { + property var rootMenu: OverteControls.WrappedMenu { id: rootMenuId objectName: "rootMenu" property var exclusionGroups: ({}); property Component exclusiveGroupMaker: Component { - ExclusiveGroup { + //ExclusiveGroup { //QT6TODO + ButtonGroup { } } + function addMenuWrap(menu) { + return addMenu(menu); + } + function addExclusionGroup(qmlAction, exclusionGroup) { var exclusionGroupId = exclusionGroup.toString(); @@ -68,6 +75,7 @@ FocusScope { exclusionGroups[exclusionGroupId] = exclusiveGroupMaker.createObject(rootMenuId); } + //QT6TODO: qmlAction.exclusiveGroup = exclusionGroups[exclusionGroupId] } } @@ -532,12 +540,13 @@ FocusScope { return customInputDialogBuilder.createObject(desktop, properties); } - Component { id: fileDialogBuilder; FileDialog { } } + Component { id: fileDialogBuilder; OverteCompat.CompatFileDialog { } } function fileDialog(properties) { return fileDialogBuilder.createObject(desktop, properties); } - Component { id: assetDialogBuilder; AssetDialog { } } + // TODO: is this actually used? + Component { id: assetDialogBuilder; Item {}}//AssetDialog { } } function assetDialog(properties) { return assetDialogBuilder.createObject(desktop, properties); } @@ -582,7 +591,7 @@ FocusScope { ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 } } - QQC2.Action { + Action { text: "Toggle Focus Debugger" shortcut: "Ctrl+Shift+F" enabled: DebugQML diff --git a/interface/resources/qml/dialogs/AssetDialog.qml b/interface/resources/qml/dialogs/AssetDialog.qml index b8eaab0b8df..694c37740cd 100644 --- a/interface/resources/qml/dialogs/AssetDialog.qml +++ b/interface/resources/qml/dialogs/AssetDialog.qml @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +import QtCore import QtQuick 2.5 -import Qt.labs.settings 1.0 import stylesUit 1.0 import "../windows" diff --git a/interface/resources/qml/dialogs/CustomQueryDialog.qml b/interface/resources/qml/dialogs/CustomQueryDialog.qml index 2497781db0d..3839313453f 100644 --- a/interface/resources/qml/dialogs/CustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/CustomQueryDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.7; -import QtQuick.Dialogs 1.2 as OriginalDialogs; +import QtQuick.Dialogs as OriginalDialogs; import QtQuick.Controls 2.3 import controlsUit 1.0 diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index fa4d911b653..c575058f39a 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -8,12 +8,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 -import Qt.labs.folderlistmodel 2.2 -import Qt.labs.settings 1.0 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import QtQuick.Controls 1.4 as QQC1 -import QtQuick.Controls 2.3 +import QtCore +import QtQuick +import Qt.labs.folderlistmodel +import Qt.labs.qmlmodels +import QtQuick.Dialogs as OriginalDialogs +import QtQuick.Controls import ".." import controlsUit 1.0 @@ -34,7 +34,7 @@ ModalWindow { HifiConstants { id: hifi } - property var filesModel: ListModel { } + property var filesModel: TableModel { } Settings { category: "FileDialog" @@ -76,8 +76,8 @@ ModalWindow { selected(button); destroy(); } - - property int clickedButton: OriginalDialogs.StandardButton.NoButton; + + property int clickedButton: OriginalDialogs.MessageDialog.NoButton; Component.onCompleted: { fileDialogItem.keyboardEnabled = HMD.active; @@ -261,7 +261,7 @@ ModalWindow { QtObject { id: d property var currentSelectionUrl; - readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl); + readonly property string currentSelectionPath: currentSelectionUrl === undefined ? "" : helper.urlToPath(currentSelectionUrl); property bool currentSelectionIsFolder; property var backStack: [] property var tableViewConnection: Connections { target: fileTableView; function onCurrentRowChanged() { d.update(); } } @@ -311,8 +311,9 @@ ModalWindow { } function clearSelection() { - fileTableView.selection.clear(); - fileTableView.currentRow = -1; + // QT6TODO + //fileTableView.selection.clear(); + //fileTableView.currentRow = -1; update(); } } @@ -374,7 +375,25 @@ ModalWindow { Component { id: filesModelBuilder - ListModel { } + TableModel { + TableModelColumn { + //id: fileNameColumn + display: "fileName" + //title: "Name" + //width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width + } + TableModelColumn { + //id: fileModifiedColumn + display: "fileModified" + //title: "Date" + //width: 0.3 * fileTableView.width + } + TableModelColumn { + display: "fileSize" + //title: "Size" + //width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width + } + } } QtObject { @@ -429,11 +448,11 @@ ModalWindow { if (row === -1) { return false; } - return filesModel.get(row).fileIsDir; + return filesModel.getRow(row).fileIsDir; } function get(row) { - return filesModel.get(row) + return filesModel.getRow(row) } function update() { @@ -473,7 +492,7 @@ ModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { + if (comparisonFunction(sortValue, filesModel.getRow(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -482,7 +501,7 @@ ModalWindow { } } - filesModel.insert(lower, { + filesModel.insertRow(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -496,9 +515,10 @@ ModalWindow { } } - Table { + TableView { id: fileTableView - colorScheme: hifi.colorSchemes.light + // QT6TODO: + //colorScheme: hifi.colorSchemes.light anchors { top: navControls.bottom topMargin: hifi.dimensions.contentSpacing.y @@ -507,15 +527,17 @@ ModalWindow { bottom: currentSelection.top bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } - headerVisible: !selectDirectory - onDoubleClicked: navigateToRow(row); + // QT6TODO: + //headerVisible: !selectDirectory + //onDoubleClicked: navigateToRow(row); Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); - sortIndicatorColumn: 0 - sortIndicatorOrder: Qt.AscendingOrder - sortIndicatorVisible: true + property int sortIndicatorColumn: 0 + property var sortIndicatorOrder: Qt.AscendingOrder + property bool sortIndicatorVisible: true + // QT6TODO model: filesModel function updateSort() { @@ -528,7 +550,20 @@ ModalWindow { onSortIndicatorOrderChanged: { updateSort(); } - itemDelegate: Item { + delegate: TextInput { + text: model.display + padding: 12 + selectByMouse: true + + onAccepted: model.display = text + + Rectangle { + anchors.fill: parent + color: "#efefef" + z: -1 + } + } + /*delegate: Item { clip: true FiraSansSemiBold { @@ -571,33 +606,35 @@ ModalWindow { return size + " " + suffixes[suffixIndex]; } } - } - - QQC1.TableViewColumn { - id: fileNameColumn - role: "fileName" - title: "Name" - width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width - movable: false - resizable: true - } - QQC1.TableViewColumn { - id: fileModifiedColumn - role: "fileModified" - title: "Date" - width: 0.3 * fileTableView.width - movable: false - resizable: true - visible: !selectDirectory - } - QQC1.TableViewColumn { - role: "fileSize" - title: "Size" - width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width - movable: false - resizable: true - visible: !selectDirectory - } + }*/ + + /*model: TableModel { + TableModelColumn { + id: fileNameColumn + role: "fileName" + title: "Name" + width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width + movable: false + resizable: true + } + TableModelColumn { + id: fileModifiedColumn + role: "fileModified" + title: "Date" + width: 0.3 * fileTableView.width + movable: false + resizable: true + visible: !selectDirectory + } + TableModelColumn { + role: "fileSize" + title: "Size" + width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width + movable: false + resizable: true + visible: !selectDirectory + } + }*/ function navigateToRow(row) { currentRow = row; @@ -651,7 +688,7 @@ ModalWindow { onTriggered: fileTableView.prefix = ""; } - Keys.onPressed: { + Keys.onPressed: event => { switch (event.key) { case Qt.Key_Backspace: case Qt.Key_Tab: @@ -820,7 +857,7 @@ ModalWindow { } } - Keys.onPressed: { + Keys.onPressed: event => { switch (event.key) { case Qt.Key_Backspace: event.accepted = d.navigateUp(); diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index 629027ab2af..ed272669d7b 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import controlsUit 1.0 import stylesUit 1.0 @@ -36,26 +36,26 @@ ModalWindow { return OffscreenUi.waitForMessageBoxResult(root); } - Keys.onRightPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onRightPressed: if (defaultButton === OriginalDialogs.MessageDialog.Yes) { yesButton.forceActiveFocus() - } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.MessageDialog.Ok) { okButton.forceActiveFocus() } - Keys.onTabPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) { + Keys.onTabPressed: if (defaultButton === OriginalDialogs.MessageDialog.Yes) { yesButton.forceActiveFocus() - } else if (defaultButton === OriginalDialogs.StandardButton.Ok) { + } else if (defaultButton === OriginalDialogs.MessageDialog.Ok) { okButton.forceActiveFocus() } property alias detailedText: detailedText.text property alias text: mainTextContainer.text property alias informativeText: informativeTextContainer.text - property int buttons: OriginalDialogs.StandardButton.Ok + property int buttons: OriginalDialogs.MessageDialog.Ok property int icon: OriginalDialogs.StandardIcon.NoIcon property string iconText: "" property int iconSize: 50 onIconChanged: updateIcon(); - property int defaultButton: OriginalDialogs.StandardButton.NoButton; - property int clickedButton: OriginalDialogs.StandardButton.NoButton; + property int defaultButton: OriginalDialogs.MessageDialog.NoButton; + property int clickedButton: OriginalDialogs.MessageDialog.NoButton; property int titleWidth: 0 onTitleWidthChanged: d.resize(); @@ -136,41 +136,41 @@ ModalWindow { margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y } - MessageDialogButton { dialog: root; text: qsTr("Close"); button: OriginalDialogs.StandardButton.Close; } - MessageDialogButton { dialog: root; text: qsTr("Abort"); button: OriginalDialogs.StandardButton.Abort; } - MessageDialogButton { dialog: root; text: qsTr("Cancel"); button: OriginalDialogs.StandardButton.Cancel; } - MessageDialogButton { dialog: root; text: qsTr("Restore Defaults"); button: OriginalDialogs.StandardButton.RestoreDefaults; } - MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; } - MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; } - MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; } + MessageDialogButton { dialog: root; text: qsTr("Close"); button: OriginalDialogs.MessageDialog.Close; } + MessageDialogButton { dialog: root; text: qsTr("Abort"); button: OriginalDialogs.MessageDialog.Abort; } + MessageDialogButton { dialog: root; text: qsTr("Cancel"); button: OriginalDialogs.MessageDialog.Cancel; } + MessageDialogButton { dialog: root; text: qsTr("Restore Defaults"); button: OriginalDialogs.MessageDialog.RestoreDefaults; } + MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.MessageDialog.Reset; } + MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.MessageDialog.Discard; } + MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.MessageDialog.NoToAll; } MessageDialogButton { id: noButton dialog: root text: qsTr("No") - button: OriginalDialogs.StandardButton.No + button: OriginalDialogs.MessageDialog.No KeyNavigation.left: yesButton KeyNavigation.backtab: yesButton } - MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; } + MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.MessageDialog.YesToAll; } MessageDialogButton { id: yesButton dialog: root text: qsTr("Yes") - button: OriginalDialogs.StandardButton.Yes + button: OriginalDialogs.MessageDialog.Yes KeyNavigation.right: noButton KeyNavigation.tab: noButton } - MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; } - MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; } - MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; } - MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; } - MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; } - MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; } + MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.MessageDialog.Apply; } + MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.MessageDialog.Ignore; } + MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.MessageDialog.Retry; } + MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.MessageDialog.SaveAll; } + MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.MessageDialog.Save; } + MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.MessageDialog.Open; } MessageDialogButton { id: okButton dialog: root text: qsTr("OK") - button: OriginalDialogs.StandardButton.Ok + button: OriginalDialogs.MessageDialog.Ok } Button { @@ -180,7 +180,7 @@ ModalWindow { onClicked: { content.state = (content.state === "" ? "expanded" : "") } visible: detailedText && detailedText.length > 0 } - MessageDialogButton { dialog: root; text: qsTr("Help"); button: OriginalDialogs.StandardButton.Help; } + MessageDialogButton { dialog: root; text: qsTr("Help"); button: OriginalDialogs.MessageDialog.Help; } } Item { @@ -256,7 +256,7 @@ ModalWindow { case Qt.Key_Escape: case Qt.Key_Back: event.accepted = true - root.click(OriginalDialogs.StandardButton.Cancel) + root.click(OriginalDialogs.MessageDialog.Cancel) break } } diff --git a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml index 3b3f7fb2d74..bece7b980c7 100644 --- a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml +++ b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml @@ -11,7 +11,7 @@ import Hifi 1.0 import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs Item { Component.onCompleted: { diff --git a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml index c7772984abd..a9566e12f9c 100644 --- a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.7 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import QtQuick.Controls 2.3 import controlsUit 1.0 diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 4ee53d44ef9..a5f912d367f 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -11,8 +11,7 @@ import QtQuick 2.7 import Qt.labs.folderlistmodel 2.2 import Qt.labs.settings 1.0 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import QtQuick.Controls 1.4 as QQC1 +import QtQuick.Dialogs as OriginalDialogs import QtQuick.Controls 2.3 import ".." @@ -460,7 +459,7 @@ TabletModalWindow { } } - Table { + TableView { id: fileTableView colorScheme: hifi.colorSchemes.light anchors { @@ -472,14 +471,14 @@ TabletModalWindow { bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } headerVisible: !selectDirectory - onDoubleClicked: navigateToRow(row); + //onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); - sortIndicatorColumn: 0 - sortIndicatorOrder: Qt.AscendingOrder - sortIndicatorVisible: true + property int sortIndicatorColumn: 0 + property var sortIndicatorOrder: Qt.AscendingOrder + property bool sortIndicatorVisible: true model: filesModel @@ -493,7 +492,7 @@ TabletModalWindow { onSortIndicatorOrderChanged: { updateSort(); } - itemDelegate: Item { + delegate: Item { clip: true FiraSansSemiBold { @@ -538,7 +537,7 @@ TabletModalWindow { } } - QQC1.TableViewColumn { + /*QQC1.TableViewColumn { id: fileNameColumn role: "fileName" title: "Name" @@ -562,7 +561,7 @@ TabletModalWindow { movable: false resizable: true visible: !selectDirectory - } + }*/ function navigateToRow(row) { currentRow = row; diff --git a/interface/resources/qml/dialogs/TabletMessageBox.qml b/interface/resources/qml/dialogs/TabletMessageBox.qml index 4411651a0f0..a28ff5575e3 100644 --- a/interface/resources/qml/dialogs/TabletMessageBox.qml +++ b/interface/resources/qml/dialogs/TabletMessageBox.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import controlsUit 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index 8f63730b8e9..4724ac28162 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.7 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import QtQuick.Controls 2.3 import controlsUit 1.0 diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml index eaf1d72fea2..6eacd58c816 100644 --- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml +++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml @@ -10,7 +10,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import QtQuick.Controls 1.5 as QQC1 +import QtQuick.Controls 2.3 as QQC1 import controlsUit 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index f5715fa2c22..a791b1d0406 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -9,13 +9,13 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs import controlsUit 1.0 Button { property var dialog; - property int button: StandardButton.Ok; + property int button: MessageDialog.Ok; color: focus ? hifi.buttons.blue : hifi.buttons.white onClicked: dialog.click(button) diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index 48d29405b02..f668a22a64e 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick import controlsUit 1.0 import "../../hifi/tablet/tabletWindows/preferences" @@ -63,8 +63,8 @@ Preference { placeholderText: root.placeholderText text: preference.value colorScheme: dataTextField.acceptableInput ? hifi.colorSchemes.dark : hifi.colorSchemes.light - validator: RegExpValidator { - regExp: /.*\.(?:fst).*\?*/ig + validator: RegularExpressionValidator { + regularExpression: /.*\.(?:fst).*\?*/ig } anchors { left: parent.left diff --git a/interface/resources/qml/dialogs/preferences/Section.qml b/interface/resources/qml/dialogs/preferences/Section.qml index a9b755ad83f..038738cacbd 100644 --- a/interface/resources/qml/dialogs/preferences/Section.qml +++ b/interface/resources/qml/dialogs/preferences/Section.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import Hifi 1.0 import controlsUit 1.0 as HiFiControls diff --git a/interface/resources/qml/hifi/+android_interface/ActionBar.qml b/interface/resources/qml/hifi/+android_interface/ActionBar.qml index 3c58156f304..4b44cd3c798 100644 --- a/interface/resources/qml/hifi/+android_interface/ActionBar.qml +++ b/interface/resources/qml/hifi/+android_interface/ActionBar.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/+android_interface/AudioBar.qml b/interface/resources/qml/hifi/+android_interface/AudioBar.qml index 912572fdf8a..c5ff5775cf0 100644 --- a/interface/resources/qml/hifi/+android_interface/AudioBar.qml +++ b/interface/resources/qml/hifi/+android_interface/AudioBar.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/+android_interface/Desktop.qml b/interface/resources/qml/hifi/+android_interface/Desktop.qml index 99d792b664f..86d09ec64da 100644 --- a/interface/resources/qml/hifi/+android_interface/Desktop.qml +++ b/interface/resources/qml/hifi/+android_interface/Desktop.qml @@ -1,5 +1,5 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import Qt.labs.settings 1.0 diff --git a/interface/resources/qml/hifi/+android_interface/StatsBar.qml b/interface/resources/qml/hifi/+android_interface/StatsBar.qml index 64e93b4a088..2435d742fba 100644 --- a/interface/resources/qml/hifi/+android_interface/StatsBar.qml +++ b/interface/resources/qml/hifi/+android_interface/StatsBar.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/+android_interface/WindowHeader.qml b/interface/resources/qml/hifi/+android_interface/WindowHeader.qml index 5316fc4786b..43448bada85 100644 --- a/interface/resources/qml/hifi/+android_interface/WindowHeader.qml +++ b/interface/resources/qml/hifi/+android_interface/WindowHeader.qml @@ -10,8 +10,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "." diff --git a/interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml b/interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml index 6b830d94c25..e459e904c1e 100644 --- a/interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml +++ b/interface/resources/qml/hifi/+android_interface/bottomHudOptions.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "../../styles" as HifiStyles diff --git a/interface/resources/qml/hifi/+android_interface/button.qml b/interface/resources/qml/hifi/+android_interface/button.qml index a4c65b3c6f1..d83dd41d123 100644 --- a/interface/resources/qml/hifi/+android_interface/button.qml +++ b/interface/resources/qml/hifi/+android_interface/button.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 Item { diff --git a/interface/resources/qml/hifi/+android_interface/modesbar.qml b/interface/resources/qml/hifi/+android_interface/modesbar.qml index 1bf04fb8d98..f20e7c8ca0c 100644 --- a/interface/resources/qml/hifi/+android_interface/modesbar.qml +++ b/interface/resources/qml/hifi/+android_interface/modesbar.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 75d7370c80a..baff11c7ded 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -8,11 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import Qt.labs.settings 1.0 +import QtQuick +import QtQuick.Controls +//import QtQuick.Controls.Styles +import QtQuick.Dialogs as OriginalDialogs +import Qt.labs.settings import stylesUit 1.0 import controlsUit 1.0 as HifiControls @@ -677,6 +677,8 @@ Windows.ScrollingWindow { font.pixelSize: hifi.fontSizes.textFieldInput height: hifi.dimensions.tableRowHeight + //QT6TODO + /* style: TextFieldStyle { textColor: readOnly ? hifi.colors.black @@ -691,10 +693,10 @@ Windows.ScrollingWindow { selectionColor: hifi.colors.primaryHighlight padding.left: readOnly ? 0 : hifi.dimensions.textPadding padding.right: readOnly ? 0 : hifi.dimensions.textPadding - } + }*/ - validator: RegExpValidator { - regExp: /[^/]+/ + validator: RegularExpressionValidator { + regularExpression: /[^/]+/ } Keys.onPressed: { diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 0a02418a8bf..582d1a468e9 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -1,8 +1,8 @@ -import QtQuick 2.6 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import QtQml.Models 2.1 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQml.Models +import Qt5Compat.GraphicalEffects import controlsUit 1.0 as HifiControls import stylesUit 1.0 import "avatarapp" diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 9fb8067371c..2ba146af976 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -13,7 +13,7 @@ import Hifi 1.0 import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import TabletScriptingInterface 1.0 import "toolbars" diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 3239471a00f..e3644504e23 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -1,5 +1,5 @@ +import QtCore import QtQuick 2.7 -import Qt.labs.settings 1.0 as QtSettings import QtQuick.Controls 2.3 @@ -12,9 +12,13 @@ import controlsUit 1.0 OriginalDesktop.Desktop { id: desktop + // QT6TODO: Desktop just eats all mouse inputs + visible: true + property alias toolbarObjectName: sysToolbar.objectName - MouseArea { + // QT6TODO: this breaks VR mouse cursor since + /*MouseArea { id: hoverWatch anchors.fill: parent hoverEnabled: true @@ -23,7 +27,7 @@ OriginalDesktop.Desktop { onEntered: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = true onExited: if (typeof ApplicationCompositor !== "undefined") ApplicationCompositor.reticleOverDesktop = false acceptedButtons: Qt.NoButton - } + }*/ Action { text: "Open Browser" @@ -79,7 +83,7 @@ OriginalDesktop.Desktop { } signal toolbarVisibleChanged(bool isVisible, string toolbarName); - QtSettings.Settings { + Settings { id: settings; category: "toolbar"; property bool constrainToolbarToCenterX: true; diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index a9fde05d8d4..8c057244617 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -13,7 +13,7 @@ import Hifi 1.0 import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "toolbars" import stylesUit 1.0 import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0dd29b9e0f4..e7f0100325f 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -10,9 +10,9 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtGraphicalEffects 1.0 +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import controlsUit 1.0 as HifiControls import "toolbars" @@ -534,10 +534,12 @@ Item { anchors.left: nameCardVUMeter.left; // Properties visible: (!isMyCard && (selected && pal.activeTab == "nearbyTab")) && isPresent; - minimumValue: -60.0 - maximumValue: 20.0 + // QT6TODO + //minimumValue: -60.0 + //maximumValue: 20.0 stepSize: 5 - updateValueWhileDragging: true + // QT6TODO + //updateValueWhileDragging: true value: Users.getAvatarGain(uuid) onValueChanged: { updateGainFromQML(uuid, value, false); @@ -565,7 +567,8 @@ Item { mouse.accepted = false } } - style: SliderStyle { + // QT6TODO + /*style: SliderStyle { groove: Rectangle { color: "#c5c5c5" implicitWidth: gainSlider.width @@ -579,7 +582,7 @@ Item { implicitWidth: 10 implicitHeight: 16 } - } + }*/ } function updateGainFromQML(avatarUuid, sliderValue, isReleased) { diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index b842b736d42..39cbaacceb8 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -12,8 +12,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtGraphicalEffects 1.0 +import QtQuick.Controls 2.3 +import Qt5Compat.GraphicalEffects import Qt.labs.settings 1.0 import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit @@ -118,6 +118,7 @@ Rectangle { comboDialog.populateComboListViewModel(); comboDialog.visible = true; } + // QT6TODO: this is deprecated Settings { id: settings; category: "pal"; @@ -427,16 +428,18 @@ Rectangle { // This TableView refers to the Nearby Table (on the "Nearby" tab below the current user's NameCard) HifiControlsUit.Table { id: nearbyTable; - flickableItem.interactive: true; + // QT6TODO + //flickableItem.interactive: true; // Anchors anchors.fill: parent; // Properties centerHeaderText: true; - sortIndicatorVisible: true; - headerVisible: true; - sortIndicatorColumn: settings.nearbySortIndicatorColumn; - sortIndicatorOrder: settings.nearbySortIndicatorOrder; - onSortIndicatorColumnChanged: { + // QT6TODO + //sortIndicatorVisible: true; + //headerVisible: true; + //sortIndicatorColumn: settings.nearbySortIndicatorColumn; + //sortIndicatorOrder: settings.nearbySortIndicatorOrder; + /*onSortIndicatorColumnChanged: { if (sortIndicatorColumn > 2) { // these are not sortable, switch back to last column sortIndicatorColumn = settings.nearbySortIndicatorColumn; @@ -444,13 +447,15 @@ Rectangle { settings.nearbySortIndicatorColumn = sortIndicatorColumn; sortModel(); } - } - onSortIndicatorOrderChanged: { + }*/ + // Qt6TODO + /*onSortIndicatorOrderChanged: { settings.nearbySortIndicatorOrder = sortIndicatorOrder; sortModel(); - } + }*/ - TableViewColumn { + // QT6TODO: how to port these? + /*TableViewColumn { role: "avgAudioLevel"; title: "LOUD"; width: actionButtonWidth; @@ -489,20 +494,22 @@ Rectangle { width: actionButtonWidth; movable: false; resizable: false; - } + }*/ model: ListModel { id: nearbyUserModel; } // This Rectangle refers to each Row in the nearbyTable. - rowDelegate: Rectangle { // The only way I know to specify a row height. + // QT6TODO + /*rowDelegate: Rectangle { // The only way I know to specify a row height. // Size height: rowHeight + (styleData.selected ? 15 : 0); color: nearbyRowColor(styleData.selected, styleData.alternate); - } + }*/ // This Item refers to the contents of each Cell - itemDelegate: Item { + // QT6TODO + /*itemDelegate: Item { id: itemCell; property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"; property bool isButton: styleData.role === "mute" || styleData.role === "kick"; @@ -664,7 +671,7 @@ Rectangle { : hifi.buttons.disabledTextColor[actionButton.colorScheme]; } } - } + }*/ } // Separator between user and admin functions @@ -824,24 +831,27 @@ Rectangle { // This TableView refers to the Connections Table (on the "Connections" tab below the current user's NameCard) HifiControlsUit.Table { id: connectionsTable; - flickableItem.interactive: true; + // QT6TODO + //flickableItem.interactive: true; visible: !connectionsLoading.visible; // Anchors anchors.fill: parent; // Properties centerHeaderText: true; - sortIndicatorVisible: true; - headerVisible: true; - sortIndicatorColumn: settings.connectionsSortIndicatorColumn; + // QT6TODO + //sortIndicatorVisible: true; + //headerVisible: true; + /*sortIndicatorColumn: settings.connectionsSortIndicatorColumn; sortIndicatorOrder: settings.connectionsSortIndicatorOrder; onSortIndicatorColumnChanged: { settings.connectionsSortIndicatorColumn = sortIndicatorColumn; } onSortIndicatorOrderChanged: { settings.connectionsSortIndicatorOrder = sortIndicatorOrder; - } + }*/ - TableViewColumn { + // QT6TODO: how to port these? + /*TableViewColumn { id: connectionsUserNameHeader; role: "userName"; title: connectionsUserModel.totalEntries + (connectionsUserModel.totalEntries === 1 ? " NAME" : " NAMES"); @@ -862,19 +872,21 @@ Rectangle { width: actionButtonWidth; movable: false; resizable: false; - } + }*/ model: connectionsUserModel; // This Rectangle refers to each Row in the connectionsTable. - rowDelegate: Rectangle { + // QT6TODO + /*rowDelegate: Rectangle { // Size height: rowHeight + (styleData.selected ? 15 : 0); color: connectionsRowColor(styleData.selected, styleData.alternate); - } + }*/ // This Item refers to the contents of each Cell - itemDelegate: Item { + // QT6TODO + /*itemDelegate: Item { id: connectionsItemCell; // This NameCard refers to the cell that contains a connection's UserName @@ -947,7 +959,7 @@ Rectangle { UserActivityLogger["palAction"](checked ? styleData.role : "un-" + styleData.role, model.sessionId); } } - } + }*/ } // "Make a Connection" instructions diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 7bfdf028891..c93e28b28e6 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -15,7 +15,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit diff --git a/interface/resources/qml/hifi/audio/InputPeak.qml b/interface/resources/qml/hifi/audio/InputPeak.qml index d8b166cee49..c6b5ce99900 100644 --- a/interface/resources/qml/hifi/audio/InputPeak.qml +++ b/interface/resources/qml/hifi/audio/InputPeak.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { property var peak; diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 55378589ecc..637a02adab0 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/hifi/audio/MicBarApplication.qml b/interface/resources/qml/hifi/audio/MicBarApplication.qml index d77668e5bfe..798c77f3f59 100644 --- a/interface/resources/qml/hifi/audio/MicBarApplication.qml +++ b/interface/resources/qml/hifi/audio/MicBarApplication.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml index be9fe932e0b..9d78f68147d 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml @@ -2,7 +2,7 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQml.Models 2.1 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import Hifi.AvatarPackager.AvatarProjectStatus 1.0 import "../../controlsUit" 1.0 as HifiControls import "../../stylesUit" 1.0 diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml index 2b06d7e99ee..f1b55e1b69e 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml @@ -1,7 +1,7 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Controls 2.2 as Original diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml index 21d0683fb11..58d22675ea9 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProjectCard.qml @@ -1,5 +1,5 @@ import QtQuick 2.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "../../controlsUit" 1.0 as HifiControls import "../../stylesUit" 1.0 diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml index 68f465f514f..4f5acd88734 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProjectUpload.qml @@ -1,7 +1,7 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Controls 2.2 as Original diff --git a/interface/resources/qml/hifi/avatarPackager/LoadingCircle.qml b/interface/resources/qml/hifi/avatarPackager/LoadingCircle.qml index a1fac72ae4c..1160e8474a6 100644 --- a/interface/resources/qml/hifi/avatarPackager/LoadingCircle.qml +++ b/interface/resources/qml/hifi/avatarPackager/LoadingCircle.qml @@ -1,7 +1,7 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects AnimatedImage { id: root diff --git a/interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml b/interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml index 8582b9249bd..41bb864caee 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarThumbnail.qml @@ -1,5 +1,5 @@ import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { width: 92 diff --git a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml index 5d72bef43da..666fbf0b932 100644 --- a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml +++ b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml @@ -13,7 +13,7 @@ Rectangle { property string titleText: 'Create Favorite' property string favoriteNameText: favoriteName.text - property string avatarImageUrl: null + property string avatarImageUrl: "" property int wearablesCount: 0 property string button1color: hifi.buttons.noneBorderlessGray; diff --git a/interface/resources/qml/hifi/avatarapp/MessageBox.qml b/interface/resources/qml/hifi/avatarapp/MessageBox.qml index 88f7f888cb0..ecfb193a2a7 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBox.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBox.qml @@ -16,7 +16,7 @@ Rectangle { property alias inputText: input; property alias dialogButtons: buttons - property string imageSource: null + property string imageSource: "" property string button1color: hifi.buttons.noneBorderlessGray; property string button1text: '' @@ -70,6 +70,7 @@ Rectangle { width: Math.max(parent.width * 0.8, 400) property int margin: 30; + // QT6TODO: QML QQuickRectangle*: Binding loop detected for property "height" height: childrenRect.height + margin * 2 onHeightChanged: { console.debug('mainContainer: height = ', height) diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index f0acbd68e78..a466272a2fd 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -1,4 +1,4 @@ -import QtQuick 2.5 +import QtQuick MessageBox { id: popup diff --git a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml index a2c84fad472..7cf7b2ac03d 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml @@ -1,6 +1,6 @@ import stylesUit 1.0 import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { property alias text: glyph.text diff --git a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml index 51e10437025..1b66bfa20d0 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml @@ -1,6 +1,6 @@ import stylesUit 1.0 import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { property alias source: image.source diff --git a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml index 3968fcb1ff2..a719627509c 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml @@ -1,6 +1,6 @@ import stylesUit 1.0 import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { property alias color: rectangle.color diff --git a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml index 69aff47373c..2615df7f783 100644 --- a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml +++ b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml @@ -1,7 +1,7 @@ import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit import QtQuick 2.9 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { id: root diff --git a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml index c8ac5f47782..7af7d554ed1 100644 --- a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml +++ b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick Item { property alias source: sourceImage.sourceItem @@ -15,7 +15,8 @@ Item { hideSource: true } - ShaderEffect { + // QT6TODO: this needs to be ported to Qt6 - probably converting into .qsb is needed +/* ShaderEffect { id: maskEffect anchors.fill: parent @@ -40,5 +41,5 @@ void main() } " } - } + }*/ } \ No newline at end of file diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index b9cfe113f08..8c52ce41d19 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -9,8 +9,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 2.3 +import QtQuick.Dialogs as OriginalDialogs import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 6374e85ada5..c9e2572a24a 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -9,9 +9,9 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 2.3 +//import QtQuick.Controls.Styles +import QtQuick.Dialogs as OriginalDialogs import Qt.labs.settings 1.0 import stylesUit 1.0 @@ -675,6 +675,8 @@ Rectangle { font.pixelSize: hifi.fontSizes.textFieldInput height: hifi.dimensions.tableRowHeight + //QT6TODO + /* style: TextFieldStyle { textColor: readOnly ? hifi.colors.black @@ -689,10 +691,10 @@ Rectangle { selectionColor: hifi.colors.primaryHighlight padding.left: readOnly ? 0 : hifi.dimensions.textPadding padding.right: readOnly ? 0 : hifi.dimensions.textPadding - } + }*/ - validator: RegExpValidator { - regExp: /[^/]+/ + validator: RegularExpressionValidator { + regularExpression: /[^/]+/ } Keys.onPressed: { diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 4aeeada1e5a..9bb91f20c13 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -9,8 +9,8 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 2.3 +import QtQuick.Dialogs as OriginalDialogs import Qt.labs.settings 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 8942a451970..a8bb5874681 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -13,7 +13,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 as HifiStylesUit import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml index f3fbb88c078..dce25783938 100644 --- a/interface/resources/qml/hifi/overlays/ImageOverlay.qml +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -1,5 +1,5 @@ import QtQuick 2.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/AvatarAppListDelegate.qml b/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/AvatarAppListDelegate.qml index a14f5d7bdef..1cd5003a568 100644 --- a/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/AvatarAppListDelegate.qml +++ b/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/AvatarAppListDelegate.qml @@ -12,7 +12,7 @@ import QtQuick 2.10 import "../../simplifiedConstants" as SimplifiedConstants import "../../simplifiedControls" as SimplifiedControls import stylesUit 1.0 as HifiStylesUit -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Rectangle { id: root diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/DisplayNameHeader.qml b/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/DisplayNameHeader.qml index e039734e731..e032150b0b5 100644 --- a/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/DisplayNameHeader.qml +++ b/interface/resources/qml/hifi/simplifiedUI/avatarApp/components/DisplayNameHeader.qml @@ -13,7 +13,7 @@ import "../../simplifiedConstants" as SimplifiedConstants import "../../simplifiedControls" as SimplifiedControls import stylesUit 1.0 as HifiStylesUit import controlsUit 1.0 as HifiControlsUit -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { id: root diff --git a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml index 9e02820168e..a893f397158 100644 --- a/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml +++ b/interface/resources/qml/hifi/simplifiedUI/inputDeviceButton/InputDeviceButton.qml @@ -10,7 +10,7 @@ // import QtQuick 2.10 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import TabletScriptingInterface 1.0 import "../simplifiedConstants" as SimplifiedConstants diff --git a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/InputPeak.qml b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/InputPeak.qml index c8440dc7d64..89d8f62d7fa 100644 --- a/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/InputPeak.qml +++ b/interface/resources/qml/hifi/simplifiedUI/simplifiedControls/InputPeak.qml @@ -9,7 +9,7 @@ // import QtQuick 2.10 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects Item { property var peak diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml index 1f0cd9008b2..20a9f77d24b 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml @@ -14,7 +14,7 @@ import "../simplifiedConstants" as SimplifiedConstants import "../inputDeviceButton" as InputDeviceButton import stylesUit 1.0 as HifiStylesUit import TabletScriptingInterface 1.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. Rectangle { diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index 6b2aa331e8d..13e713388a9 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -8,7 +8,7 @@ import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import "../../controls" diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index ee4865258e4..0e2df1a4e98 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQuick.Window 2.2 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import Qt.labs.settings 1.0 import stylesUit 1.0 import "../../controls" diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 20a68237cf3..8fdb8b478ef 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Controls 2.2 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/tablet/TADLightbox.qml b/interface/resources/qml/hifi/tablet/TADLightbox.qml index 35a01aeec31..abfd3a19ca9 100644 --- a/interface/resources/qml/hifi/tablet/TADLightbox.qml +++ b/interface/resources/qml/hifi/tablet/TADLightbox.qml @@ -13,7 +13,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index fdb05c8a35f..96858c855b8 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtQuick.Controls.Styles 1.4 -import QtGraphicalEffects 1.0 +//import QtQuick.Controls.Styles +import Qt5Compat.GraphicalEffects import "../../controls" import "../../styles" import "../../windows" diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index bea2d31e2bd..2b1afce685d 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -1,5 +1,5 @@ import QtQuick 2.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import TabletScriptingInterface 1.0 Item { diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 0311a4fa13c..c7b693cf282 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -1,6 +1,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Layouts 1.3 import TabletScriptingInterface 1.0 @@ -200,7 +200,7 @@ Item { Repeater { id: pageRepeater model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : 0 - onItemAdded: { + onItemAdded: (index, item) => { item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null; item.proxyModel.pageIndex = index; } @@ -284,7 +284,7 @@ Item { Connections { target: modelData; - onPropertiesChanged: { + function onPropertiesChanged() { updateProperties(); } } diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index 5f06e4fbab7..bc97b515c7d 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 +import Qt5Compat.GraphicalEffects +import QtQuick.Controls 2.3 import QtQml 2.2 @@ -15,7 +15,7 @@ FocusScope { width: parent.width height: parent.height - property var rootMenu: Menu { objectName:"rootMenu" } + property var rootMenu: WrappedMenu { objectName:"rootMenu" } property var point: Qt.point(50, 50); TabletMenuStack { id: menuPopperUpper } property string subMenu: "" diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml index 25db90c771a..a127c79161f 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import controlsUit 1.0 import stylesUit 1.0 diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 76d170cba84..2e427b016af 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "." diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 5e90696adc6..cc11fb7c617 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -27,30 +27,30 @@ Rectangle { option = value; } - Component { id: inputDialogBuilder; TabletQueryDialog { } } + Component { id: inputDialogBuilder; Item {}}//TabletQueryDialog { } } function inputDialog(properties) { openModal = inputDialogBuilder.createObject(tabletRoot, properties); return openModal; } - Component { id: messageBoxBuilder; TabletMessageBox { } } + Component { id: messageBoxBuilder; Item {}}//TabletMessageBox { } } function messageBox(properties) { openMessage = messageBoxBuilder.createObject(tabletRoot, properties); return openMessage; } - Component { id: customInputDialogBuilder; TabletCustomQueryDialog { } } + Component { id: customInputDialogBuilder; Item {}}//TabletCustomQueryDialog { } } function customInputDialog(properties) { openModal = customInputDialogBuilder.createObject(tabletRoot, properties); return openModal; } - Component { id: fileDialogBuilder; TabletFileDialog { } } + Component { id: fileDialogBuilder; Item { }}//TabletFileDialog { } } function fileDialog(properties) { openModal = fileDialogBuilder.createObject(tabletRoot, properties); return openModal; } - Component { id: assetDialogBuilder; TabletAssetDialog { } } + Component { id: assetDialogBuilder; Item { }}//TabletAssetDialog { } } function assetDialog(properties) { openModal = assetDialogBuilder.createObject(tabletRoot, properties); return openModal; @@ -287,7 +287,7 @@ Rectangle { } width: 480 - height: 706 + height: 720 function setShown(value) { if (value === true) { diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index dcdc711936e..4f3d10aee92 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -11,11 +11,10 @@ // TODO: FIXME: this is practically identical to TabletRoot.qml import "../../windows" as Windows +import QtCore import QtQuick 2.0 import Hifi 1.0 -import Qt.labs.settings 1.0 - Windows.ScrollingWindow { id: tabletRoot objectName: "tabletRoot" @@ -32,7 +31,7 @@ Windows.ScrollingWindow { id: settings category: "WindowRoot.Windows" property real width: 480 - property real height: 706 + property real height: 720 } onResizableChanged: { @@ -41,7 +40,7 @@ Windows.ScrollingWindow { settings.width = tabletRoot.width settings.height = tabletRoot.height tabletRoot.width = 480 - tabletRoot.height = 706 + tabletRoot.height = 720 } else { tabletRoot.width = settings.width tabletRoot.height = settings.height @@ -205,7 +204,6 @@ Windows.ScrollingWindow { } } - implicitWidth: 480 - implicitHeight: 706 + implicitHeight: 720 } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index 91dc6fe5684..796a93fa39d 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import Qt.labs.folderlistmodel 2.2 import Qt.labs.settings 1.0 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import QtQuick.Controls 1.4 as QQC1 +import QtQuick.Dialogs as OriginalDialogs +import QtQuick.Controls 2.3 as QQC1 import QtQuick.Controls 2.3 import ".." @@ -466,15 +466,17 @@ Rectangle { bottom: currentSelection.top bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } - headerVisible: !selectDirectory - onDoubleClicked: navigateToRow(row); + // QT6TODO: doesn't exist on Qt 6. + //headerVisible: !selectDirectory + //onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); - sortIndicatorColumn: 0 - sortIndicatorOrder: Qt.AscendingOrder - sortIndicatorVisible: true + // QT6TODO: doesn't exist on Qt 6. + //sortIndicatorColumn: 0 + //sortIndicatorOrder: Qt.AscendingOrder + //sortIndicatorVisible: true model: filesModel @@ -484,11 +486,14 @@ Rectangle { fileTableModel.update(); } - onSortIndicatorColumnChanged: { updateSort(); } + // QT6TODO: doesn't exist on Qt 6. + //onSortIndicatorColumnChanged: { updateSort(); } - onSortIndicatorOrderChanged: { updateSort(); } + // QT6TODO: doesn't exist on Qt 6. + //onSortIndicatorOrderChanged: { updateSort(); } - itemDelegate: Item { + // QT6TODO: How to port this? + /*itemDelegate: Item { clip: true FiraSansSemiBold { @@ -531,9 +536,10 @@ Rectangle { return size + " " + suffixes[suffixIndex]; } } - } + }*/ - QQC1.TableViewColumn { + // QT6TODO: I have no idea how to port this + /*QQC1.TableViewColumn { id: fileNameColumn role: "fileName" title: "Name" @@ -557,7 +563,7 @@ Rectangle { movable: false resizable: true visible: !selectDirectory - } + }*/ function navigateToRow(row) { currentRow = row; diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index 57fdeb482b7..2af1857e5cf 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick import Hifi 1.0 import "../../../../dialogs/preferences" diff --git a/interface/resources/qml/hifi/toolbars/Toolbar.qml b/interface/resources/qml/hifi/toolbars/Toolbar.qml index 56028d71f05..b4db726566d 100644 --- a/interface/resources/qml/hifi/toolbars/Toolbar.qml +++ b/interface/resources/qml/hifi/toolbars/Toolbar.qml @@ -1,5 +1,5 @@ +import QtCore import QtQuick 2.5 -import Qt.labs.settings 1.0 import "../../windows" import "." diff --git a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml index 303712b4f70..f503e93f0f3 100644 --- a/interface/resources/qml/hifi/toolbars/ToolbarButton.qml +++ b/interface/resources/qml/hifi/toolbars/ToolbarButton.qml @@ -1,5 +1,7 @@ import QtQuick 2.5 +// TODO: figure out a way of hooking these up to +// the theme constants without too much breakage StateImage { id: button diff --git a/interface/resources/qml/overte/BodyText.qml b/interface/resources/qml/overte/BodyText.qml new file mode 100644 index 00000000000..303301567bd --- /dev/null +++ b/interface/resources/qml/overte/BodyText.qml @@ -0,0 +1,29 @@ +import QtQuick +import QtQuick.Controls + +import "." + +TextEdit { + selectByMouse: true + readOnly: true + + font.pixelSize: Theme.fontPixelSize + font.family: Theme.bodyFontFamily + color: Theme.paletteActive.text + + textFormat: TextEdit.PlainText + wrapMode: TextEdit.Wrap + + selectedTextColor: Theme.paletteActive.highlightedText + selectionColor: Theme.paletteActive.highlight + + // TODO: should we continue supporting the in-game browser + // or should we transition to always using the system one? + // Qt doesn't make it easy to theme rich text + onLinkActivated: link => Qt.openUrlExternally(link) + + HoverHandler { + enabled: parent.hoveredLink + cursorShape: Qt.PointingHandCursor + } +} diff --git a/interface/resources/qml/overte/Button.qml b/interface/resources/qml/overte/Button.qml new file mode 100644 index 00000000000..bcc255b0fbb --- /dev/null +++ b/interface/resources/qml/overte/Button.qml @@ -0,0 +1,55 @@ +import QtQuick +import QtQuick.Controls +import "." + +Button { + id: button + property color backgroundColor: Theme.paletteActive.button + property color color: Theme.paletteActive.buttonText + + palette.buttonText: color + font.family: Theme.fontFamily + font.pixelSize: Theme.fontPixelSize + horizontalPadding: 12 + verticalPadding: 8 + hoverEnabled: true + + opacity: enabled ? 1.0 : 0.5 + + background: Rectangle { + opacity: flat ? 0.0 : 1.0 + + id: buttonBg + radius: Theme.borderRadius + border.width: button.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: ( + button.activeFocus ? + Theme.paletteActive.focusRing : + ( + Theme.highContrast ? + Theme.paletteActive.buttonText : + Qt.darker(button.backgroundColor, Theme.borderDarker) + ) + ) + color: { + if (button.down || button.checked) { + return Qt.darker(button.backgroundColor, Theme.checkedDarker); + } else if (button.hovered && button.enabled) { + return Qt.lighter(button.backgroundColor, Theme.hoverLighter); + } else { + return button.backgroundColor; + } + } + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(buttonBg.color, (button.down || button.checked) ? 0.9 : 1.1) + } + GradientStop { + position: 0.5; color: buttonBg.color + } + GradientStop { + position: 1.0; color: Qt.darker(buttonBg.color, (button.down || button.checked) ? 0.9 : 1.1) + } + } + } +} diff --git a/interface/resources/qml/overte/ComboBox.qml b/interface/resources/qml/overte/ComboBox.qml new file mode 100644 index 00000000000..89a6741ad93 --- /dev/null +++ b/interface/resources/qml/overte/ComboBox.qml @@ -0,0 +1,141 @@ +import QtQuick +import QtQuick.Controls +import "." + +ComboBox { + id: control + + property color backgroundColor: flat ? "#00000000" : Theme.paletteActive.window + property color color: Theme.paletteActive.windowText + + implicitHeight: Theme.fontPixelSize * 2 + + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + + indicator: Button { + anchors.right: control.right + width: control.height + height: control.height + + horizontalPadding: 2 + verticalPadding: 2 + focusPolicy: Qt.NoFocus + flat: control.flat + + icon.source: "./icons/triangle_down.svg" + icon.width: 24 + icon.height: 24 + icon.color: Theme.paletteActive.buttonText + + onClicked: { + control.forceActiveFocus(); + + if (control.popup.opened) { + control.popup.close(); + } else { + control.popup.open(); + } + } + } + + contentItem: Text { + leftPadding: 6 + rightPadding: control.indicator.width + control.spacing + text: control.displayText + font: control.font + color: control.color + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + opacity: enabled ? 1.0 : 0.5 + } + + background: Rectangle { + opacity: enabled ? 1.0 : 0.5 + width: (control.width - control.indicator.width) + Theme.borderWidth + radius: Theme.borderRadius + border.width: control.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: { + if (control.activeFocus) { + return Theme.paletteActive.focusRing; + } else if (control.flat) { + return "#00000000"; + } else if (Theme.highContrast) { + return parent.color; + } else { + return Qt.darker(control.backgroundColor, Theme.borderDarker); + } + } + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.darker(control.backgroundColor, control.flat ? 1.0 : 1.02) } + GradientStop { position: 0.5; color: control.backgroundColor } + GradientStop { position: 1.0; color: Qt.lighter(control.backgroundColor, control.flat ? 1.0 : 1.02) } + } + } + + delegate: ItemDelegate { + id: delegate + + required property var model + required property int index + + width: control.width + contentItem: Text { + text: delegate.model[control.textRole] + color: highlighted ? Theme.paletteActive.highlightedText : Theme.paletteActive.tooltipText + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + color: highlighted ? Theme.paletteActive.highlight : Theme.paletteActive.tooltip + } + + highlighted: control.highlightedIndex === index + } + + popup: Popup { + y: control.height - Theme.borderWidth + width: control.width + (contentItem.ScrollBar.vertical.opacity > 0.0 ? contentItem.ScrollBar.vertical.width : 0) + height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin) + padding: 3 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + (parent.padding * 2) + model: control.popup.visible ? control.delegateModel : null + currentIndex: control.highlightedIndex + + ScrollBar.vertical: ScrollBar { + interactive: false + } + } + + background: Item { + // drop shadow + Rectangle { + x: 3 + y: 3 + width: parent.width + height: parent.height + + radius: Theme.borderRadius + color: "#a0000000" + } + + Rectangle { + x: 0 + y: 0 + width: parent.width + height: parent.height + + radius: Theme.borderRadius + border.width: Theme.borderWidth + border.color: Qt.darker(Theme.paletteActive.tooltip, 2.5) + color: Theme.paletteActive.tooltip + } + } + } +} + diff --git a/interface/resources/qml/overte/Dialog.qml b/interface/resources/qml/overte/Dialog.qml new file mode 100644 index 00000000000..6df695b25bb --- /dev/null +++ b/interface/resources/qml/overte/Dialog.qml @@ -0,0 +1,81 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Dialogs + +import "." + +Rectangle { + id: dialog + visible: false + + default property alias content: dialogWindow.children + + property int maxWidth: 480 + property int maxHeight: -1 + + property bool shadedBackground: true + + function open() { + visible = true; + opacity = Theme.reducedMotion ? 1 : 0; + } + + function close() { + visible = false; + opacity = 0; + } + + color: !shadedBackground ? "transparent" : Theme.paletteActive.dialogShade + + opacity: Theme.reducedMotion ? 1 : 0 + OpacityAnimator on opacity { + from: Theme.reducedMotion ? 1 : 0 + to: 1 + duration: 150 + easing.type: Easing.InQuad + running: dialog.visible + } + + // block any inputs from underneath + MouseArea { + enabled: parent.visible + anchors.fill: parent + hoverEnabled: true + } + + Rectangle { + id: dialogWindow + width: Math.min( + (maxWidth == -1 ? Infinity : maxWidth), + children[0].implicitWidth + (children[0].anchors.margins * 2), + parent.width - 8 + ) + height: Math.min( + (maxHeight == -1 ? Infinity : maxHeight), + children[0].implicitHeight + (children[0].anchors.margins * 2), + parent.height - 8 + ) + anchors.centerIn: parent + + color: Theme.paletteActive.base + radius: 8 + border.width: 2 + border.color: Theme.highContrast ? Theme.paletteActive.text : Qt.darker(Theme.paletteActive.base, Theme.borderDarker) + + OpacityAnimator on opacity { + from: Theme.reducedMotion ? 1 : 0 + to: 1 + easing.type: Easing.OutQuad + duration: 200 + running: dialog.visible + } + + ScaleAnimator on scale { + from: Theme.reducedMotion ? 1 : 0.9 + to: 1 + easing.type: Easing.OutQuad + duration: 200 + running: dialog.visible + } + } +} diff --git a/interface/resources/qml/overte/Label.qml b/interface/resources/qml/overte/Label.qml new file mode 100644 index 00000000000..86fc51694cd --- /dev/null +++ b/interface/resources/qml/overte/Label.qml @@ -0,0 +1,9 @@ +import QtQuick +import QtQuick.Controls +import "." + +Label { + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + color: Theme.paletteActive.text +} diff --git a/interface/resources/qml/overte/MessageDialog.qml b/interface/resources/qml/overte/MessageDialog.qml new file mode 100644 index 00000000000..33cd5dcde0d --- /dev/null +++ b/interface/resources/qml/overte/MessageDialog.qml @@ -0,0 +1,114 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Dialogs + +import "." + +Dialog { + id: root + implicitWidth: layout.implicitWidth + implicitHeight: layout.implicitHeight + + readonly property var buttonRoles: [ + { label: qsTr("Ok"), flag: MessageDialog.Ok, role: MessageDialog.AcceptRole }, + { label: qsTr("Save"), flag: MessageDialog.Save, role: MessageDialog.AcceptRole }, + { label: qsTr("Yes"), flag: MessageDialog.Yes, role: MessageDialog.YesRole }, + { label: qsTr("Cancel"), flag: MessageDialog.Cancel, role: MessageDialog.RejectRole }, + { label: qsTr("Discard"), flag: MessageDialog.Discard, role: MessageDialog.DestructiveRole }, + { label: qsTr("No"), flag: MessageDialog.No, role: MessageDialog.NoRole }, + ] + + property string text: "Oops! Your ModalDialog doesn't have any text." + property string descriptiveText: "" + property int buttons: MessageDialog.Ok | MessageDialog.Cancel + property int result: MessageDialog.Ok + + property list buttonDataModel: { + let list = []; + + for (let role of buttonRoles) { + if ((role.flag & buttons) !== 0) { + list.push(role); + } + } + + return list.reverse(); + } + + signal accepted + signal rejected + signal buttonClicked(button: int, role: int) + + function reject() { + rejected(); + close(); + } + + function accept() { + accepted(); + close(); + } + + onButtonClicked: (button, role) => { + result = button; + + switch (role) { + case MessageDialog.AcceptRole: + case MessageDialog.YesRole: + accept(); + break; + + case MessageDialog.RejectRole: + case MessageDialog.NoRole: + reject(); + break; + } + } + + ColumnLayout { + id: layout + anchors.fill: parent + anchors.margins: 8 + + Label { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 8 + + visible: root.text !== "" + text: root.text + wrapMode: Text.Wrap + } + + Label { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 8 + + visible: root.descriptiveText !== "" + text: root.descriptiveText + wrapMode: Text.Wrap + } + + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + + Repeater { + model: buttonDataModel + delegate: Button { + required property string label + required property int flag + required property int role + + Layout.fillWidth: true + Layout.minimumWidth: 96 + Layout.preferredWidth: 96 + + text: label + onClicked: buttonClicked(flag, role); + } + } + } + } +} diff --git a/interface/resources/qml/overte/README.md b/interface/resources/qml/overte/README.md new file mode 100644 index 00000000000..4fb88540c35 --- /dev/null +++ b/interface/resources/qml/overte/README.md @@ -0,0 +1,19 @@ +# TODO List + +## Critical +- [ ] Virtual keyboard support for text fields +- [x] Asset browser +- [x] Running scripts window +- [ ] Actually saving the color scheme and contrast settings + +## High +- [x] Avatar picker +- [x] People app +- [x] World list + +## Low +- [x] More app +- [ ] Desktop apps bar/tablet app picker +- [ ] Create app +- [ ] Dashboard +- [ ] Hook up chat bubbles background opacity to high contrast mode diff --git a/interface/resources/qml/overte/RoundButton.qml b/interface/resources/qml/overte/RoundButton.qml new file mode 100644 index 00000000000..5deec714cde --- /dev/null +++ b/interface/resources/qml/overte/RoundButton.qml @@ -0,0 +1,52 @@ +import QtQuick +import QtQuick.Controls +import "." + +Button { + id: button + property color backgroundColor: Theme.paletteActive.button + + palette.buttonText: Theme.paletteActive.buttonText + font.family: Theme.fontFamily + font.pixelSize: Theme.fontPixelSize + horizontalPadding: 2 + verticalPadding: 2 + hoverEnabled: true + implicitHeight: font.pixelSize * 2 + implicitWidth: font.pixelSize * 2 + + background: Rectangle { + id: buttonBg + radius: button.height / 2 + border.width: parent.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: ( + button.activeFocus ? + Theme.paletteActive.focusRing : + ( + Theme.highContrast ? + Theme.paletteActive.buttonText : + Qt.darker(button.backgroundColor, Theme.borderDarker) + ) + ) + color: ( + (button.down || button.checked) ? + Qt.darker(button.backgroundColor, Theme.checkedDarker) : + ( + (button.hovered && button.enabled) ? + Qt.lighter(button.backgroundColor, Theme.hoverLighter) : + button.backgroundColor + ) + ) + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(buttonBg.color, (button.down || button.checked) ? 0.9 : 1.1) + } + GradientStop { + position: 0.5; color: buttonBg.color + } + GradientStop { + position: 1.0; color: Qt.darker(buttonBg.color, (button.down || button.checked) ? 0.9 : 1.1) + } + } + } +} diff --git a/interface/resources/qml/overte/Ruler.qml b/interface/resources/qml/overte/Ruler.qml new file mode 100644 index 00000000000..522c6208169 --- /dev/null +++ b/interface/resources/qml/overte/Ruler.qml @@ -0,0 +1,29 @@ +import QtQuick + +import "." + +Item { + property color color: ( + Theme.highContrast ? + Theme.paletteActive.text : + Theme.paletteActive.base + ) + + implicitHeight: 4 + + Rectangle { + x: 0 + y: 0 + height: Math.floor(parent.height / 2) + width: parent.width + color: Qt.darker(parent.color, Theme.depthDarker) + } + + Rectangle { + x: 0 + y: Math.floor(parent.height / 2) + height: Math.floor(parent.height / 2) + width: parent.width + color: Qt.lighter(parent.color, Theme.depthLighter) + } +} diff --git a/interface/resources/qml/overte/ScrollBar.qml b/interface/resources/qml/overte/ScrollBar.qml new file mode 100644 index 00000000000..6fa5d7f985c --- /dev/null +++ b/interface/resources/qml/overte/ScrollBar.qml @@ -0,0 +1,150 @@ +import QtQuick +import QtQuick.Controls +import "." + +ScrollBar { + id: control + opacity: { + if ( + control.policy === ScrollBar.AlwaysOn || + (control.policy === ScrollBar.AsNeeded && interactive && control.size < 1.0) + ) { + return 1.0; + } else if (control.active && control.size < 1.0) { + return 0.75; + } else { + return 0.0; + } + } + + Behavior on opacity { + NumberAnimation {} + } + + property color backgroundColor: Theme.paletteActive.button + + readonly property bool hasButtons: Theme.scrollbarButtons && interactive + readonly property int thumbWidth: Theme.scrollbarWidth - (horizontalPadding * 2) + readonly property bool isHorizontal: orientation === Qt.Horizontal + + //horizontalPadding: Theme.borderWidth + //verticalPadding: Theme.borderWidth + horizontalPadding: 0 + verticalPadding: 0 + + // TODO: magic numbers? minimumSize is weird and confusing, + // 0.32 doesn't work on short scrollbars + stepSize: 0.03 + minimumSize: 0.32 + + background: Rectangle { + color: Qt.darker( + Theme.paletteActive.base, + Theme.highContrast ? 1.0 : (Theme.darkMode ? 1.2 : 1.1) + ) + implicitWidth: Theme.scrollbarWidth + implicitHeight: Theme.scrollbarWidth + } + + contentItem: Item { + implicitWidth: horizontal ? 32 : thumbWidth + implicitHeight: horizontal ? thumbWidth : 32 + + Rectangle { + // pad away from the scroll buttons, but allow a border width of sink-in + // so there isn't a double-border when the thumbs are at their min/max + anchors.topMargin: control.hasButtons && !control.isHorizontal ? thumbWidth - Theme.borderWidth : 0 + anchors.bottomMargin: control.hasButtons && !control.isHorizontal ? thumbWidth - Theme.borderWidth: 0 + anchors.leftMargin: control.hasButtons && control.isHorizontal ? thumbWidth - Theme.borderWidth: 0 + anchors.rightMargin: control.hasButtons && control.isHorizontal ? thumbWidth - Theme.borderWidth : 0 + anchors.fill: parent + + id: buttonBg + radius: Theme.borderRadius + border.width: Theme.borderWidth + border.color: Theme.highContrast ? Theme.paletteActive.buttonText : Qt.darker(control.backgroundColor, Theme.borderDarker) + color: Theme.highContrast ? Theme.paletteActive.buttonText : control.backgroundColor; + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(buttonBg.color, 1.05) + } + GradientStop { + position: 0.5; color: buttonBg.color + } + GradientStop { + position: 1.0; color: Qt.darker(buttonBg.color, 1.05) + } + } + } + } + + Button { + anchors.right: !control.isHorizontal ? control.right : undefined + anchors.rightMargin: !control.isHorizontal ? control.horizontalPadding : undefined + + anchors.left: control.isHorizontal ? control.left : undefined + anchors.leftMargin: control.isHorizontal ? control.horizontalPadding : undefined + + anchors.top: control.top + anchors.topMargin: control.verticalPadding + + width: thumbWidth + height: width + + id: scrollLessButton + visible: control.hasButtons + focusPolicy: Qt.NoFocus + + icon.source: ( + !control.isHorizontal ? + "./icons/triangle_up.svg" : + "./icons/triangle_left.svg" + ) + icon.width: width - 4 + icon.height: height - 4 + icon.color: Theme.paletteActive.buttonText + display: AbstractButton.IconOnly + horizontalPadding: 0 + verticalPadding: 0 + autoRepeat: true + + onClicked: { + control.position = Math.max(0.0, control.position - control.stepSize); + } + } + + Button { + anchors.right: control.isHorizontal ? control.right : undefined + anchors.rightMargin: control.isHorizontal ? control.horizontalPadding : undefined + + anchors.left: !control.isHorizontal ? control.left : undefined + anchors.leftMargin: !control.isHorizontal ? control.horizontalPadding : undefined + + anchors.bottom: control.bottom + anchors.bottomMargin: control.verticalPadding + + width: thumbWidth + height: width + + id: scrollMoreButton + visible: control.hasButtons + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + horizontalPadding: 0 + verticalPadding: 0 + autoRepeat: true + + icon.source: ( + !control.isHorizontal ? + "./icons/triangle_down.svg" : + "./icons/triangle_right.svg" + ) + icon.width: width - 4 + icon.height: height - 4 + icon.color: Theme.paletteActive.buttonText + + onClicked: { + control.position = Math.min(1.0 - control.size, control.position + control.stepSize); + } + } +} diff --git a/interface/resources/qml/overte/Slider.qml b/interface/resources/qml/overte/Slider.qml new file mode 100644 index 00000000000..89ce00528e7 --- /dev/null +++ b/interface/resources/qml/overte/Slider.qml @@ -0,0 +1,70 @@ +import QtQuick +import QtQuick.Controls + +import "." + +Slider { + id: control + + background: Rectangle { + y: control.topPadding + control.availableHeight / 2 - height / 2 + implicitWidth: 200 + height: 8 + radius: height / 2 + color: Theme.paletteActive.base + + border.width: Theme.borderWidth + border.color: Qt.darker(color, Theme.borderDarker) + + Rectangle { + width: control.visualPosition * parent.width + height: parent.height + radius: parent.radius + color: Theme.paletteActive.highlight + + border.color: Qt.darker(color, Theme.borderDarker) + border.width: Theme.borderWidth + } + } + + handle: Rectangle { + implicitWidth: 26 + implicitHeight: 26 + radius: height / 2 + + x: control.leftPadding + control.visualPosition * (control.availableWidth - width) + y: control.topPadding + control.availableHeight / 2 - height / 2 + + id: handle + border.width: control.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: ( + control.activeFocus ? + Theme.paletteActive.focusRing : + ( + Theme.highContrast ? + Theme.paletteActive.buttonText : + Qt.darker(Theme.paletteActive.button, Theme.borderDarker) + ) + ) + color: { + if (control.hovered && control.enabled) { + return Qt.lighter(Theme.paletteActive.button, Theme.hoverLighter); + } else if (!control.enabled) { + return Theme.paletteActive.base; + } else { + return Theme.paletteActive.button; + } + } + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(handle.color, control.enabled ? 1.1 : 1.0) + } + GradientStop { + position: 0.5; color: handle.color + } + GradientStop { + position: 1.0; color: Qt.darker(handle.color, control.enabled ? 1.1 : 1.0) + } + } + } +} diff --git a/interface/resources/qml/overte/SpinBox.qml b/interface/resources/qml/overte/SpinBox.qml new file mode 100644 index 00000000000..79ef1f16d4c --- /dev/null +++ b/interface/resources/qml/overte/SpinBox.qml @@ -0,0 +1,143 @@ +import QtQuick +import QtQuick.Controls + +import "." + +SpinBox { + id: control + + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + + background: Rectangle { + anchors.left: control.down.indicator.right + anchors.right: control.up.indicator.left + anchors.leftMargin: -Theme.borderWidth + anchors.rightMargin: -Theme.borderWidth + + implicitHeight: Theme.fontPixelSize * 2 + implicitWidth: 180 + // no radius so the borders can mix into the scroll button borders + //radius: Theme.borderRadius + border.width: control.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: ( + parent.activeFocus ? + Theme.paletteActive.focusRing : + ( + Theme.highContrast ? + Theme.paletteActive.windowText : + Qt.darker(Theme.paletteActive.window, Theme.borderDarker) + ) + ) + color: Theme.paletteActive.window + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.darker(control.background.color, 1.02) } + GradientStop { position: 0.5; color: control.background.color } + GradientStop { position: 1.0; color: Qt.lighter(control.background.color, 1.02) } + } + } + + contentItem: TextInput { + anchors.left: control.down.indicator.right + anchors.right: control.up.indicator.left + + font: control.font + color: Theme.paletteActive.windowText + selectionColor: Theme.paletteActive.highlight + selectedTextColor: Theme.paletteActive.highlightedText + text: control.textFromValue(control.value, control.locale) + + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + + readOnly: !control.editable + validator: control.validator + inputMethodHints: control.inputMethodHints + } + + down.indicator: Rectangle{ + id: downIndicator + x: 0 + width: Theme.fontPixelSize * 2 + height: parent.height + color: ( + control.down.down ? + Qt.darker(Theme.paletteActive.button, Theme.checkedDarker) : + ( + (control.down.hovered && control.enabled) ? + Qt.lighter(Theme.paletteActive.button, Theme.hoverLighter) : + Theme.paletteActive.button + ) + ) + opacity: control.value > control.from ? 1.0 : 0.5 + + radius: 0 + topLeftRadius: Theme.borderRadius + bottomLeftRadius: Theme.borderRadius + + border.width: Theme.borderWidth + border.color: ( + Theme.highContrast ? + Theme.paletteActive.windowText : + Qt.darker(Theme.paletteActive.button, Theme.borderDarker) + ) + + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.lighter(downIndicator.color, 1.05) } + GradientStop { position: 0.5; color: downIndicator.color } + GradientStop { position: 1.0; color: Qt.darker(downIndicator.color, 1.05) } + } + + Text { + anchors.fill: parent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + color: Theme.paletteActive.buttonText + font: control.font + text: "-" + } + } + + up.indicator: Rectangle{ + id: upIndicator + x: parent.width - width + width: Theme.fontPixelSize * 2 + height: parent.height + color: ( + control.up.down ? + Qt.darker(Theme.paletteActive.button, Theme.checkedDarker) : + ( + (control.up.hovered && control.enabled) ? + Qt.lighter(Theme.paletteActive.button, Theme.hoverLighter) : + Theme.paletteActive.button + ) + ) + opacity: control.value < control.to ? 1.0 : 0.5 + + radius: 0 + topRightRadius: Theme.borderRadius + bottomRightRadius: Theme.borderRadius + + border.width: Theme.borderWidth + border.color: ( + Theme.highContrast ? + Theme.paletteActive.windowText : + Qt.darker(Theme.paletteActive.button, Theme.borderDarker) + ) + + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.lighter(upIndicator.color, 1.05) } + GradientStop { position: 0.5; color: upIndicator.color } + GradientStop { position: 1.0; color: Qt.darker(upIndicator.color, 1.05) } + } + + Text { + anchors.fill: parent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + color: Theme.paletteActive.buttonText + font: control.font + text: "+" + } + } +} diff --git a/interface/resources/qml/overte/StackView.qml b/interface/resources/qml/overte/StackView.qml new file mode 100644 index 00000000000..197a5931107 --- /dev/null +++ b/interface/resources/qml/overte/StackView.qml @@ -0,0 +1,74 @@ +import QtQuick +import QtQuick.Controls + +import "." + +// a stack view with reasonable defaults for the +// animations, respecting the theme reduced motion settings +StackView { + pushEnter: Transition { + PropertyAnimation { + property: "opacity" + from: Theme.reducedMotion ? 0 : 1 + to: 1 + duration: 80 + } + + PropertyAnimation { + property: "x" + from: Theme.reducedMotion ? 0 : width + to: 0 + duration: 350 + easing.type: Easing.OutQuad + } + } + pushExit: Transition { + PropertyAnimation { + property: "opacity" + from: 1 + to: Theme.reducedMotion ? 0 : 1 + duration: 80 + } + + PropertyAnimation { + property: "x" + to: Theme.reducedMotion ? 0 : -width + from: 0 + duration: 350 + easing.type: Easing.OutQuad + } + } + + popEnter: Transition { + PropertyAnimation { + property: "opacity" + from: Theme.reducedMotion ? 0 : 1 + to: 1 + duration: 80 + } + + PropertyAnimation { + property: "x" + from: Theme.reducedMotion ? 0 : -width + to: 0 + duration: 350 + easing.type: Easing.OutQuad + } + } + popExit: Transition { + PropertyAnimation { + property: "opacity" + from: 1 + to: Theme.reducedMotion ? 0 : 1 + duration: 80 + } + + PropertyAnimation { + property: "x" + to: Theme.reducedMotion ? 0 : width + from: 0 + duration: 350 + easing.type: Easing.OutQuad + } + } +} diff --git a/interface/resources/qml/overte/Switch.qml b/interface/resources/qml/overte/Switch.qml new file mode 100644 index 00000000000..5ff772162ed --- /dev/null +++ b/interface/resources/qml/overte/Switch.qml @@ -0,0 +1,74 @@ +import QtQuick +import QtQuick.Controls +import "." + +Switch { + id: control + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + opacity: control.enabled ? 1.0 : 0.5 + + Rectangle { + anchors.fill: indicator + anchors.margins: -Theme.borderWidthFocused + color: Theme.paletteActive.focusRing + visible: control.activeFocus + radius: indicator.radius + } + + indicator: Rectangle { + implicitWidth: 48 + implicitHeight: 24 + x: control.leftPadding + y: parent.height / 2 - height / 2 + radius: 24 + color: { + if (Theme.highContrast) { + return control.checked ? Theme.paletteActive.buttonText : Theme.paletteActive.button; + } else if (control.checked) { + return Theme.paletteActive.highlight; + } else { + return Qt.darker(Theme.paletteActive.base, Theme.checkedDarker); + } + } + border.color: ( + Theme.highContrast ? + Theme.paletteActive.buttonText : + Qt.darker(color, Theme.borderDarker) + ) + border.width: Theme.borderWidth + + Rectangle { + x: control.checked ? parent.width - width : 0 + width: 24 + height: 24 + radius: 24 + color: ( + control.down ? + Qt.darker(Theme.paletteActive.button, Theme.depthDarker) : + ( + control.hovered && control.enabled ? + Qt.lighter(Theme.paletteActive.button, Theme.depthLighter) : + Theme.paletteActive.button + ) + ); + border.color: { + if (Theme.highContrast) { + return Theme.paletteActive.buttonText; + } else { + return Qt.darker(Theme.paletteActive.button, Theme.borderDarker); + } + } + border.width: Theme.borderWidth + } + } + + contentItem: Text { + text: control.text + font: control.font + color: Theme.paletteActive.text + opacity: enabled ? 1.0 : 0.3 + verticalAlignment: Text.AlignVCenter + leftPadding: control.indicator.width + control.spacing + } +} diff --git a/interface/resources/qml/overte/TabBar.qml b/interface/resources/qml/overte/TabBar.qml new file mode 100644 index 00000000000..9f7a3c60fec --- /dev/null +++ b/interface/resources/qml/overte/TabBar.qml @@ -0,0 +1,22 @@ +import QtQuick +import QtQuick.Controls +import "." + +TabBar { + id: tabBar + spacing: 2 + clip: true + + background: Item { + Rectangle { anchors.fill: parent; color: Theme.paletteActive.base } + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: Theme.borderWidth + color: Qt.darker(Theme.paletteActive.base) + } + } + + implicitHeight: Theme.fontPixelSize + 16 +} diff --git a/interface/resources/qml/overte/TabButton.qml b/interface/resources/qml/overte/TabButton.qml new file mode 100644 index 00000000000..a3a4bb550fc --- /dev/null +++ b/interface/resources/qml/overte/TabButton.qml @@ -0,0 +1,56 @@ +import QtQuick +import QtQuick.Controls +import "." + +TabButton { + id: button + property color backgroundColor: Theme.paletteActive.base + + readonly property int borderWidth: checked ? Theme.borderWidth * 2 : Theme.borderWidth + + font.family: Theme.fontFamily + font.pixelSize: Theme.fontPixelSize + horizontalPadding: 12 + verticalPadding: 8 + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.topMargin: checked ? 2 : 6 + anchors.bottomMargin: -borderWidth + implicitHeight: Theme.fontPixelSize + 16 + + contentItem: Text { + text: button.text + font: button.font + color: Theme.paletteActive.text + opacity: enabled ? 1.0 : 0.3 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + id: buttonBg + border.width: parent.borderWidth + border.color: { + if (parent.activeFocus) { + return Theme.paletteActive.focusRing; + } else if (parent.checked) { + return Theme.paletteActive.highlight; + } else if (Theme.highContrast) { + return Theme.paletteActive.buttonText; + } else { + return Qt.darker(parent.backgroundColor, Theme.borderDarker); + } + } + color: parent.backgroundColor; + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(buttonBg.color, 1.2) + } + GradientStop { + position: 1.0; color: buttonBg.color + } + } + } +} diff --git a/interface/resources/qml/overte/TextArea.qml b/interface/resources/qml/overte/TextArea.qml new file mode 100644 index 00000000000..785b1f3ea99 --- /dev/null +++ b/interface/resources/qml/overte/TextArea.qml @@ -0,0 +1,35 @@ +import QtQuick +import QtQuick.Controls +import "." + +TextArea { + id: textArea + selectByMouse: true + + property color backgroundColor: Theme.paletteActive.window + + color: Theme.paletteActive.windowText + placeholderTextColor: Theme.paletteActive.placeholderText + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + selectionColor: Theme.paletteActive.highlight + selectedTextColor: Theme.paletteActive.highlightedText + + leftPadding: 6 + rightPadding: 6 + topPadding: 8 + bottomPadding: 8 + + background: Rectangle { + radius: Theme.borderRadius + border.width: parent.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: textArea.activeFocus ? + Theme.paletteActive.focusRing : + (Theme.highContrast ? Theme.paletteActive.windowText : Qt.darker(textArea.backgroundColor, Theme.borderDarker)) + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.darker(textArea.backgroundColor, 1.02) } + GradientStop { position: 0.5; color: textArea.backgroundColor } + GradientStop { position: 1.0; color: Qt.lighter(textArea.backgroundColor, 1.02) } + } + } +} diff --git a/interface/resources/qml/overte/TextField.qml b/interface/resources/qml/overte/TextField.qml new file mode 100644 index 00000000000..303c8cdee47 --- /dev/null +++ b/interface/resources/qml/overte/TextField.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls +import "." + +TextField { + id: textField + selectByMouse: true + + property color backgroundColor: Theme.paletteActive.window + + color: Theme.paletteActive.windowText + placeholderTextColor: Theme.paletteActive.placeholderText + font.pixelSize: Theme.fontPixelSize + font.family: Theme.fontFamily + selectionColor: Theme.paletteActive.highlight + selectedTextColor: Theme.paletteActive.highlightedText + + opacity: enabled ? 1.0 : 0.5 + + leftPadding: 6 + rightPadding: 6 + topPadding: 8 + bottomPadding: 8 + + background: Rectangle { + implicitHeight: Theme.fontPixelSize * 2 + radius: Theme.borderRadius + border.width: parent.activeFocus ? Theme.borderWidthFocused : Theme.borderWidth + border.color: parent.activeFocus ? + Theme.paletteActive.focusRing : + (Theme.highContrast ? Theme.paletteActive.windowText : Qt.darker(parent.backgroundColor, Theme.borderDarker)) + gradient: Gradient { + GradientStop { position: 0.0; color: Qt.darker(textField.backgroundColor, 1.02) } + GradientStop { position: 0.5; color: textField.backgroundColor } + GradientStop { position: 1.0; color: Qt.lighter(textField.backgroundColor, 1.02) } + } + } +} diff --git a/interface/resources/qml/overte/Theme.qml b/interface/resources/qml/overte/Theme.qml new file mode 100644 index 00000000000..5c05fdfc911 --- /dev/null +++ b/interface/resources/qml/overte/Theme.qml @@ -0,0 +1,236 @@ +pragma Singleton +import QtCore +import QtQml +import QtQuick + +QtObject { + property bool darkMode: _settings.darkMode // WindowScriptingInterface.darkMode + property bool highContrast: _settings.highContrast // WindowScriptingInterface.highContrast + property bool reducedMotion: _settings.reducedMotion // WindowScriptingInterface.reducedMotion + + // FIXME: singletons can't access context properties? + readonly property Settings _settings: Settings { + category: "Theme" + property bool darkMode: true + property bool highContrast: false + property bool reducedMotion: false + } + + // font face for UI elements + readonly property string fontFamily: "Roboto" + + // font face for document text + readonly property string bodyFontFamily: "Roboto" + + // font face for code editors + readonly property string monoFontFamily: "Roboto Mono" + + readonly property int fontPixelSize: 18 + readonly property int fontPixelSizeSmall: 14 + readonly property int fontPixelSizeXSmall: 11 + + readonly property real borderRadius: 4.0 + readonly property real borderWidth: 2.0 + readonly property real borderWidthFocused: highContrast ? borderWidth * 2 : borderWidth + + // TODO: set these on mobile where scroll buttons aren't useful + readonly property int scrollbarWidth: 24 + readonly property bool scrollbarButtons: true + + // Qt.lighten and Qt.darken constants for subtle 3D effect + readonly property real borderDarker: darkMode ? 2.5 : 1.5 + readonly property real depthLighter: highContrast ? 1.0 : 1.2 + readonly property real depthDarker: highContrast ? 1.0 : 1.3 + readonly property real checkedDarker: highContrast ? 1.5 : 1.2 + readonly property real hoverLighter: { + if (darkMode) { + // don't apply hover lightness on dark high contrast + return highContrast ? 1.0 : 1.3; + } else { + // 1.3 blows out the button colors to white on the light theme + return 1.1; + } + } + + readonly property var paletteActive: ( + highContrast ? + (darkMode ? paletteDarkContrast : paletteLightContrast) : + (darkMode ? paletteDark : paletteLight) + ) + + readonly property var paletteDark: QtObject { + readonly property color alternateBase: Qt.darker(base, 1.1) + readonly property color base: "#403849" + readonly property color text: "#eeeeee" + readonly property color button: "#605868" + readonly property color buttonText: "#eeeeee" + readonly property color window: "#524c59" + readonly property color windowText: "#eeeeee" + readonly property color highlight: "#0a9dce" + readonly property color highlightedText: "#ffffff" + + readonly property color focusRing: "#f4801a" + readonly property color placeholderText: "#80eeeeee" + + readonly property color tooltip: "#524c59" + readonly property color tooltipText: "#eeeeee" + + readonly property color buttonDestructive: "#823d3d" + readonly property color buttonAdd: "#3a753a" + readonly property color buttonInfo: "#1e6591" + readonly property color buttonFavorite: "#b28c01" + + readonly property color statusOffline: "#808080" + readonly property color statusFriendsOnly: "orange" + readonly property color statusContacts: "lime" + readonly property color statusEveryone: "cyan" + + readonly property color link: highlight + readonly property color dialogShade: "#d0000000" + + readonly property color activeWindowTitleBg: Qt.darker("#403849", 1.2) + readonly property color activeWindowTitleFg: text + + readonly property color userCountEmpty: "#b0b0b0" + readonly property color userCountActive: "#22ef22" + readonly property color userCountFull: "#ef2f1f" + + readonly property color appIconBackground: "#202020" + readonly property color appInstalledRunning: statusContacts + readonly property color appInstalledNotRunning: statusFriendsOnly + readonly property color appNotInstalledRunning: "red" + readonly property color appNotInstalled: statusOffline + } + + readonly property var paletteLight: QtObject { + readonly property color alternateBase: Qt.darker(base, 1.05) + readonly property color base: "#f5f5f5" + readonly property color text: "#111111" + readonly property color button: "#f3f2f4" + readonly property color buttonText: "#111111" + readonly property color window: "#eeeeee" + readonly property color windowText: "#111111" + readonly property color highlight: "#0b3ebf" + readonly property color highlightedText: "#ffffff" + + readonly property color focusRing: "#f4801a" + readonly property color placeholderText: "#60000000" + + readonly property color tooltip: "#fffecc" + readonly property color tooltipText: "#111111" + + readonly property color buttonDestructive: "#fccccc" + readonly property color buttonAdd: "#bef4c5" + readonly property color buttonInfo: "#bfe5fc" + readonly property color buttonFavorite: "#eddda6" + + readonly property color statusOffline: "#808080" + readonly property color statusFriendsOnly: "brown" + readonly property color statusContacts: "green" + readonly property color statusEveryone: "teal" + + readonly property color link: highlight + readonly property color dialogShade: "#d0808080" + + readonly property color activeWindowTitleBg: "#000080" + readonly property color activeWindowTitleFg: "white" + + readonly property color userCountEmpty: "#303030" + readonly property color userCountActive: "#008000" + readonly property color userCountFull: "#800000" + + readonly property color appIconBackground: "#202020" + readonly property color appInstalledRunning: "#00ff00" + readonly property color appInstalledNotRunning: "#ffaf00" + readonly property color appNotInstalledRunning: "red" + readonly property color appNotInstalled: statusOffline + } + + readonly property var paletteDarkContrast: QtObject { + readonly property color alternateBase: Qt.darker(base, 1.1) + readonly property color base: "#000000" + readonly property color text: "#f0f0f0" + readonly property color button: "#000000" + readonly property color buttonText: "#f0f0f0" + readonly property color window: "#000000" + readonly property color windowText: "#f0f0f0" + readonly property color highlight: "#ffffff" + readonly property color highlightedText: "#000000" + + readonly property color focusRing: "#ff00ff" + readonly property color placeholderText: "#00ff00" + + readonly property color tooltip: "#000000" + readonly property color tooltipText: "#ffff00" + + readonly property color buttonDestructive: "#600000" + readonly property color buttonAdd: "#006000" + readonly property color buttonInfo: "#000080" + readonly property color buttonFavorite: "#606000" + + readonly property color statusOffline: "#808080" + readonly property color statusFriendsOnly: "orange" + readonly property color statusContacts: "lime" + readonly property color statusEveryone: "cyan" + + readonly property color link: "#ffff00" + readonly property color dialogShade: "#e8000000" + + readonly property color activeWindowTitleBg: base + readonly property color activeWindowTitleFg: "white" + + readonly property color userCountEmpty: text + readonly property color userCountActive: "#00ff00" + readonly property color userCountFull: "#ff00ff" + + readonly property color appIconBackground: "black" + readonly property color appInstalledRunning: statusContacts + readonly property color appInstalledNotRunning: statusFriendsOnly + readonly property color appNotInstalledRunning: "red" + readonly property color appNotInstalled: statusOffline + } + + readonly property var paletteLightContrast: QtObject { + readonly property color alternateBase: Qt.darker(base, 1.05) + readonly property color base: "#f5f5f5" + readonly property color text: "#000000" + readonly property color button: "#f5f5f5" + readonly property color buttonText: "#000000" + readonly property color window: "#f0f0f0" + readonly property color windowText: "#000000" + readonly property color highlight: "#600060" + readonly property color highlightedText: "#ffffff" + + readonly property color focusRing: "#ff00ff" + readonly property color placeholderText: "#007000" + + readonly property color tooltip: "#fffeee" + readonly property color tooltipText: "#111111" + + readonly property color buttonDestructive: "#ffdddd" + readonly property color buttonAdd: "#ddffdd" + readonly property color buttonInfo: "#ddffff" + readonly property color buttonFavorite: "#ffffdd" + + readonly property color statusOffline: "#808080" + readonly property color statusFriendsOnly: "brown" + readonly property color statusContacts: "green" + readonly property color statusEveryone: "teal" + + readonly property color link: "#000080" + readonly property color dialogShade: "#fad0d0d0" + + readonly property color activeWindowTitleBg: base + readonly property color activeWindowTitleFg: "black" + + readonly property color userCountEmpty: text + readonly property color userCountActive: "#006000" + readonly property color userCountFull: "#600060" + + readonly property color appIconBackground: "black" + readonly property color appInstalledRunning: "#00ff00" + readonly property color appInstalledNotRunning: "#ffaf00" + readonly property color appNotInstalledRunning: "red" + readonly property color appNotInstalled: statusOffline + } +} diff --git a/interface/resources/qml/overte/ToolTip.qml b/interface/resources/qml/overte/ToolTip.qml new file mode 100644 index 00000000000..01391f8204f --- /dev/null +++ b/interface/resources/qml/overte/ToolTip.qml @@ -0,0 +1,47 @@ +import QtQuick +import QtQuick.Controls +import "." + +ToolTip { + id: control + delay: 500 + + // NOTE: this default is for Controls, + // if you're using something else then override this + visible: parent.hovered + + font.family: Theme.fontFamily + font.pixelSize: Theme.fontPixelSizeSmall + + background: Item { + // drop shadow + Rectangle { + x: 3 + y: 3 + width: parent.width + height: parent.height + + radius: Theme.borderRadius + color: "#a0000000" + } + + Rectangle { + x: 0 + y: 0 + width: parent.width + height: parent.height + + radius: Theme.borderRadius + border.width: Theme.borderWidth + border.color: Theme.highContrast ? Theme.paletteActive.tooltipText : Qt.darker(Theme.paletteActive.tooltip, Theme.borderDarker) + color: Theme.paletteActive.tooltip + } + } + + contentItem: Text { + text: control.text + font: control.font + color: Theme.paletteActive.tooltipText + wrapMode: Text.Wrap + } +} diff --git a/interface/resources/qml/overte/WidgetZoo.qml b/interface/resources/qml/overte/WidgetZoo.qml new file mode 100644 index 00000000000..c2d08878a50 --- /dev/null +++ b/interface/resources/qml/overte/WidgetZoo.qml @@ -0,0 +1,148 @@ +import QtQuick +import QtQuick.Window +import QtQuick.Layouts +import QtQuick.Controls + +import "." as Overte + +// debugging test case to view the themed widgets +Rectangle { + id: root + width: 480 + height: 720 + visible: true + color: Overte.Theme.paletteActive.base + + Overte.TabBar { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + id: tabBar + Overte.TabButton { text: "Tab 1"; width: 120 } + Overte.TabButton { text: "Tab 2"; width: 120 } + Overte.TabButton { text: "Tab 3"; width: 120 } + Overte.TabButton { text: "Tab 4"; width: 120 } + Overte.TabButton { text: "Tab 5"; width: 120 } + Overte.TabButton { text: "Tab 6"; width: 120 } + Overte.TabButton { text: "Tab 7"; width: 120 } + Overte.TabButton { text: "Tab 8"; width: 120 } + } + + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabBar.bottom + anchors.leftMargin: 16 + anchors.rightMargin: 16 + anchors.topMargin: 16 + spacing: 8 + + RowLayout { + Layout.fillWidth: true + spacing: 8 + Overte.Button { + text: "Button" + } + Overte.Button { + text: "Destroy" + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + } + Overte.Button { + text: "Add" + backgroundColor: Overte.Theme.paletteActive.buttonAdd + } + Overte.Button { + text: "Info" + backgroundColor: Overte.Theme.paletteActive.buttonInfo + } + Overte.RoundButton { + icon.source: "./icons/folder.svg" + icon.width: 24 + icon.height: 24 + } + } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: "Text field" + } + Overte.TextArea { + Layout.fillWidth: true + Layout.preferredHeight: 64 + placeholderText: "Text area" + } + Overte.TextArea { + Layout.fillWidth: true + Layout.preferredHeight: 80 + placeholderText: "Code area" + font.family: Overte.Theme.monoFontFamily + text: "function doSomething() {\n console.info(\"Hello, world\");\n}\n" + } + Overte.Switch { + text: "Switch" + } + Overte.Slider { + value: 0.5 + } + Overte.SpinBox { + editable: true + } + Overte.Ruler { Layout.fillWidth: true } + Row { + spacing: 16 + + Overte.ComboBox { + model: [ + "ComboBox", + "Singing Dogs", + "Golden Combs", + "Swirly Bombs", + ] + } + + Overte.Label { + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: "This is a note label." + } + } + + Overte.BodyText { + Layout.fillWidth: true + text: "This is body text. It's meant for selectable text documents or chat messages." + } + + ScrollView { + id: scrollView + Layout.fillWidth: true + Layout.preferredHeight: 256 + + clip: true + contentWidth: 1024 + contentHeight: 1024 + + ScrollBar.vertical: Overte.ScrollBar { + anchors.top: scrollView.top + anchors.bottom: scrollView.bottom + anchors.right: scrollView.right + anchors.bottomMargin: Theme.scrollbarWidth + } + ScrollBar.horizontal: Overte.ScrollBar { + anchors.bottom: scrollView.bottom + anchors.left: scrollView.left + anchors.right: scrollView.right + anchors.rightMargin: Theme.scrollbarWidth + } + + Image { + anchors.fill: parent + source: "./icons/unset_avatar.svg" + fillMode: Image.Tile + opacity: 0.2 + } + + Overte.Label { + text: "ScrollView and ScrollBar" + } + } + } +} diff --git a/interface/resources/qml/overte/avatar_picker/AvatarItem.qml b/interface/resources/qml/overte/avatar_picker/AvatarItem.qml new file mode 100644 index 00000000000..738e9341bd7 --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/AvatarItem.qml @@ -0,0 +1,123 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.VectorImage + +import "../" as Overte + +Item { + id: item + required property int index + required property string name + required property url avatarUrl + required property url iconUrl + required property list tags + + implicitWidth: GridView.view.cellWidth + implicitHeight: GridView.view.cellHeight + + Overte.Button { + anchors.fill: parent + anchors.margins: 4 + + id: avatarButton + + Image { + id: buttonIcon + source: item.iconUrl + fillMode: Image.PreserveAspectFit + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: buttonLabel.top + anchors.margins: 4 + + // placeholder icon + Image { + anchors.centerIn: parent + width: 64 + height: 64 + + fillMode: Image.PreserveAspectFit + source: "../icons/avatars.png" + sourceSize.width: width + sourceSize.height: height + visible: buttonIcon.status === Image.Error || buttonIcon.status === Image.Null + } + } + + Overte.Label { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: buttonLabel.top + anchors.margins: 4 + visible: buttonIcon.status === Image.Loading + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Loading…") + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 4 + + text: item.name + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignBottom + wrapMode: Text.Wrap + + id: buttonLabel + } + + onClicked: root.loadBookmark(item.name) + } + + Overte.RoundButton { + id: deleteButton + anchors.top: parent.top + anchors.left: parent.left + + visible: root.editable && (hovered || editButton.hovered || avatarButton.hovered) + opacity: hovered || Overte.Theme.highContrast ? 1.0 : 0.75 + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + + implicitWidth: 44 + implicitHeight: 44 + + icon.source: "../icons/delete.svg" + icon.width: 32 + icon.height: 32 + icon.color: Overte.Theme.paletteActive.buttonText + + Overte.ToolTip { text: qsTr("Delete") } + + onClicked: root.requestDelete(item.index, item.name) + } + + Overte.RoundButton { + id: editButton + anchors.top: parent.top + anchors.right: parent.right + + visible: root.editable && (hovered || deleteButton.hovered || avatarButton.hovered) + opacity: hovered || Overte.Theme.highContrast ? 1.0 : 0.75 + backgroundColor: Overte.Theme.paletteActive.buttonInfo + + implicitWidth: 44 + implicitHeight: 44 + + icon.source: "../icons/pencil.svg" + icon.width: 32 + icon.height: 32 + icon.color: Overte.Theme.paletteActive.buttonText + + Overte.ToolTip { text: qsTr("Edit") } + + onClicked: root.requestEdit(item.index) + } +} diff --git a/interface/resources/qml/overte/avatar_picker/AvatarPicker.qml b/interface/resources/qml/overte/avatar_picker/AvatarPicker.qml new file mode 100644 index 00000000000..609b7442488 --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/AvatarPicker.qml @@ -0,0 +1,240 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs as QtDialogs + +import "../" as Overte +import "." + +Rectangle { + id: root + anchors.fill: parent + color: Overte.Theme.paletteActive.base + implicitWidth: 480 + implicitHeight: 720 + + property bool editable: true + property list availableTags: [] + property string searchExpression: ".*" + property var avatarModel: [] + + function updateBookmarkModel() { + const data = AvatarBookmarks.getBookmarks(); + let tmp = []; + + for (const [name, avatar] of Object.entries(data)) { + let iconUrl; + + if (avatar.avatarIcon !== "") { + iconUrl = avatar.avatarIcon; + } else { + iconUrl = new URL(avatar.avatarUrl); + iconUrl.pathname = iconUrl.pathname.replace(/[.](?:fst|glb|fbx|vrm)$/i, ".jpg"); + iconUrl = iconUrl.toString(); + } + + tmp.push({ + name: name, + avatarUrl: avatar.avatarUrl, + iconUrl: iconUrl, + }); + } + + tmp.sort((a, b) => a.name.localeCompare(b.name)); + + avatarModel = tmp; + } + + Component.onCompleted: updateBookmarkModel() + + Connections { + target: AvatarBookmarks + + function onBookmarkAdded() { updateBookmarkModel(); } + function onBookmarkDeleted() { updateBookmarkModel(); } + } + + function loadBookmark(name) { + AvatarBookmarks.loadBookmark(name); + currentName.text = name; + } + + Overte.StackView { + id: stack + anchors.fill: parent + + initialItem: MainPage {} + } + + property int requestedDeleteIndex: -1 + + function requestDelete(index, name) { + requestedDeleteIndex = index; + deleteWarningDialog.text = qsTr("Are you sure you want to delete %1?").arg(name); + deleteWarningDialog.open(); + } + + function requestEdit(index) { + if (index === -1) { + // getAvatarEntitiesVariant has its own separate format, + // { id, properties: {} } + let entityDataRaw = MyAvatar.getAvatarEntitiesVariant(); + let entityData = []; + + for (let entry of entityDataRaw) { + let data = entry.properties; + data.id = entry.id; + entityData.push(data); + } + + stack.push("./EditorPage.qml", { + bookmarkToReplace: "", + avatarName: MyAvatar.getFullAvatarModelName(), + avatarURL: MyAvatar.skeletonModelURL, + avatarScale: MyAvatar.scale, + iconURL: "", + entityData: entityData, + }); + } else { + // AvatarBookmarks has its own separate format, + // { properties: { id } } + const name = avatarModel[index].name; + const data = AvatarBookmarks.getBookmark(name); + + const modelURL = data?.avatarUrl ?? ""; + const iconURL = data?.avatarIcon ?? ""; + const scale = data?.avatarScale ?? 1.0; + + // FIXME: yes, it's "avatarEntites", not "avatarEntities" + let entityData = (data?.avatarEntites ?? []).map(e => e.properties); + + stack.push("./EditorPage.qml", { + bookmarkToReplace: name, + avatarName: name, + avatarURL: modelURL, + avatarScale: scale, + iconURL: iconURL, + entityData: entityData, + }); + } + } + + Overte.MessageDialog { + id: deleteWarningDialog + anchors.fill: parent + buttons: QtDialogs.MessageDialog.Yes | QtDialogs.MessageDialog.No + + onAccepted: { + AvatarBookmarks.removeBookmark(avatarModel[requestedDeleteIndex].name); + requestedDeleteIndex = -1; + } + } + + Overte.Dialog { + id: addNewDialog + anchors.fill: parent + maxWidth: -1 + + property string avatarName: "" + property string avatarUrl: "" + + signal accepted + signal rejected + + onAccepted: { + const prevData = AvatarBookmarks.getBookmark(addNewDialog.avatarName); + + if (addNewDialog.avatarName !== avatarNameField.text) { + AvatarBookmarks.removeBookmark(addNewDialog.avatarName); + } + + // Qt's V4 doesn't support { ...spread } syntax :( + let newData = prevData; + + if (avatarUrlField.text !== "") { + newData.avatarUrl = avatarUrlField.text; + } + + AvatarBookmarks.setBookmarkData(avatarNameField.text, newData); + + avatarName = ""; + avatarUrl = ""; + avatarNameField.text = ""; + avatarUrlField.text = ""; + close(); + } + + onRejected: { + avatarName = ""; + avatarUrl = ""; + avatarNameField.text = ""; + avatarUrlField.text = ""; + close(); + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Add new avatar") + } + Overte.Ruler { Layout.fillWidth: true } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: qsTr("Avatar name") + text: addNewDialog.avatarName + id: avatarNameField + } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: qsTr("Avatar URL (.fst, .glb, .vrm, .fbx)") + text: addNewDialog.avatarUrl + id: avatarUrlField + } + + RowLayout { + Layout.preferredWidth: 720 + Layout.fillWidth: true + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + text: qsTr("Cancel") + + onClicked: addNewDialog.rejected() + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + enabled: avatarUrlField.text.trim() === "" || avatarNameField.text.trim() === "" + text: qsTr("Add Current") + + onClicked: { + avatarUrlField.text = MyAvatar.skeletonModelURL; + addNewDialog.accepted(); + } + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Add") + enabled: avatarNameField.text !== "" && avatarUrlField.text !== "" + + onClicked: addNewDialog.accepted() + } + } + } + } +} diff --git a/interface/resources/qml/overte/avatar_picker/EditorPage.qml b/interface/resources/qml/overte/avatar_picker/EditorPage.qml new file mode 100644 index 00000000000..276d683b9ee --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/EditorPage.qml @@ -0,0 +1,273 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "." + +ColumnLayout { + id: editor + + required property string bookmarkToReplace + required property string avatarName + required property string avatarURL + required property real avatarScale + required property string iconURL + required property var entityData + + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + + ScrollBar.vertical: Overte.ScrollBar { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + policy: ScrollBar.AsNeeded + } + contentWidth: width - ScrollBar.vertical.width + + component TextSetting: Column { + anchors.left: column.left + anchors.right: column.right + spacing: 4 + + required property string label + property string description + property alias text: inputField.text + property alias enabled: inputField.enabled + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.Wrap + text: parent.label + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: Overte.Theme.fontPixelSizeSmall + wrapMode: Text.Wrap + text: parent.description + visible: text !== "" + } + + Overte.TextField { + anchors.left: parent.left + anchors.right: parent.right + id: inputField + } + } + + component SliderSetting: Column { + anchors.left: column.left + anchors.right: column.right + spacing: 4 + + required property string label + property string description + property alias from: inputSlider.from + property alias to: inputSlider.to + property alias value: inputSlider.value + property alias stepSize: inputSlider.stepSize + property alias snapMode: inputSlider.snapMode + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.Wrap + text: parent.label + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: Overte.Theme.fontPixelSizeSmall + wrapMode: Text.Wrap + text: parent.description + visible: text !== "" + } + + Overte.Slider { + anchors.left: parent.left + anchors.right: parent.right + id: inputSlider + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: inputSlider.value.toFixed(1) + } + } + + Column { + anchors.fill: parent + anchors.margins: 8 + + id: column + spacing: Overte.Theme.fontPixelSize + padding: 8 + + Column { + anchors.left: column.left + anchors.right: column.right + visible: bookmarkToReplace === "" + spacing: 4 + + Overte.Label { + width: parent.width + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Editing your current avatar info") + } + + Overte.Ruler { width: parent.width } + } + + TextSetting { + id: settingName + label: qsTr("Name") + text: editor.avatarName + enabled: bookmarkToReplace !== "" + } + + TextSetting { + id: settingURL + label: qsTr("Model URL") + description: qsTr("Supported formats are FST, glTF, and FBX. FST files contain metadata about an avatar, such as its name, material overrides, and flow bone properties.") + text: editor.avatarURL + } + + TextSetting { + id: settingIconURL + label: qsTr("Icon URL (optional)") + description: qsTr("If left empty, the game will attempt to use a .jpg file with the same name and path as the model URL.") + text: editor.iconURL + } + + SliderSetting { + id: settingScale + label: qsTr("Model scale") + description: qsTr("A scale of 1 uses the avatar model's natural height. Some domains may have their own avatar scale limits. Note that very large or very small avatars may exhibit buggy behavior.") + value: editor.avatarScale + stepSize: 0.1 + from: 0.1 + to: 5.0 + } + + Column { + anchors.left: column.left + anchors.right: column.right + spacing: 4 + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.Wrap + text: qsTr("Avatar Entities") + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + font.pixelSize: Overte.Theme.fontPixelSizeSmall + wrapMode: Text.Wrap + text: qsTr("Avatar entities follow you across domains and are saved as part of the avatar settings. Avatar entities are commonly used for clothes and other wearables. You can edit your avatar entities in further detail using the Create app. Note that some domains may not allow avatar entities.") + } + + Overte.Ruler { + anchors.left: parent.left + anchors.right: parent.right + } + + EntityEditor { + anchors.left: parent.left + anchors.right: parent.right + model: editor.entityData + id: entityEditor + } + } + } + } + + RowLayout { + Layout.fillWidth: true + + Overte.Button { + // make the buttons equal size + Layout.fillWidth: true + Layout.preferredWidth: 1 + + text: qsTr("Cancel") + onClicked: stack.pop() + } + + Item { + // make the buttons equal size + Layout.fillWidth: true + Layout.preferredWidth: 1 + } + + Overte.Button { + // make the buttons equal size + Layout.fillWidth: true + Layout.preferredWidth: 1 + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: bookmarkToReplace !== "" ? qsTr("Save") : qsTr("Apply") + + onClicked: { + if (bookmarkToReplace !== "") { + // AvatarBookmarks has its own separate format, + // { properties: { id } } + let entityData = []; + + for (let item of entityEditor.model) { + entityData.push({ properties: item }); + } + + // editing an existing bookmark + AvatarBookmarks.setBookmarkData(bookmarkToReplace, { + version: 3, + avatarUrl: settingURL.text, + avatarIcon: settingIconURL.text, + avatarScale: settingScale.value, + // FIXME: yes, it's "avatarEntites", not "avatarEntities" + avatarEntites: entityData, + }); + } else { + // editing the current unsaved avatar state + MyAvatar.skeletonModelURL = settingURL.text; + MyAvatar.scale = settingScale.value; + + // marked as deprecated, but this is the only way + // of editing avatar entities in QML, since QVariantMap + // isn't convertible to EntityItemProperties + + // updateAvatarEntities has its own separate format, + // { id, properties: {} } + let entityData = []; + + for (let item of entityEditor.model) { + let props = {}; + Object.assign(props, item); + delete props.id; + + entityData.push({ + id: item.id, + properties: props, + }); + } + + AvatarBookmarks.updateAvatarEntities(entityData); + } + + stack.pop(); + } + } + } +} diff --git a/interface/resources/qml/overte/avatar_picker/EntityEditor.qml b/interface/resources/qml/overte/avatar_picker/EntityEditor.qml new file mode 100644 index 00000000000..ed58e4329c5 --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/EntityEditor.qml @@ -0,0 +1,183 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "." + +Column { + id: entityEditor + spacing: 4 + + required property var model + + Overte.Label { + width: entityEditor.width + font.pixelSize: Overte.Theme.fontPixelSizeSmall + wrapMode: Text.Wrap + text: qsTr("Currently, only Model entities can be added from here. In the future, you will be able to create any entity type through the Create app.") + } + + Overte.Label { + width: entityEditor.width + text: qsTr("Entity name") + } + + Overte.TextField { + width: entityEditor.width + id: addEntityName + } + + Overte.Label { + width: entityEditor.width + text: qsTr("Model URL") + } + + Overte.TextField { + width: entityEditor.width + id: addEntityURL + } + + RowLayout { + width: entityEditor.width + + Overte.Label { + Layout.fillWidth: true + text: qsTr("Skin model to avatar skeleton") + } + + Overte.Switch { + id: addEntitySkinned + } + } + + Overte.Label { + visible: !addEntitySkinned.checked + width: entityEditor.width + text: qsTr("Parent joint name or index") + } + + Overte.TextField { + visible: !addEntitySkinned.checked + width: entityEditor.width + id: addEntityJoint + } + + Overte.Button { + width: entityEditor.width + text: qsTr("Add entity") + + icon.source: "../icons/plus.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + enabled: addEntityName.text.trim() !== "" && addEntityURL.text.trim() !== "" + + onClicked: { + let joint = undefined; + + if (!addEntitySkinned.checked) { + joint = MyAvatar.getJointIndex(addEntityJoint.text); + + if (joint === -1) { + if (addEntityJoint.text.match(/[0-9]+/)) { + joint = parseInt(addEntityJoint.text); + } else { + joint = undefined; + } + } + } + + entityEditor.model.push({ + id: Uuid.generate(), + type: "Model", + name: addEntityName.text, + parentID: MyAvatar.SELF_ID, + parentJointIndex: joint, + modelURL: addEntityURL.text, + relayParentJoints: addEntitySkinned.checked, + useOriginalPivot: true, + }); + + // force an update on the repeater, since + // JS arrays don't trigger change signals + entityEditor.modelChanged(); + + addEntityName.text = ""; + addEntityURL.text = ""; + addEntityJoint.text = ""; + addEntitySkinned.checked = false; + } + } + + Overte.Ruler { width: entityEditor.width } + + Repeater { + model: entityEditor.model + + RowLayout { + width: entityEditor.width + + required property int index + required property var modelData + + Column { + Layout.fillWidth: true + Layout.fillHeight: true + + Overte.Label { + width: parent.width + elide: Text.ElideLeft + text: { + if (modelData.name) { + return modelData.name; + } else { + return modelData.id ?? "No ID"; + } + } + } + + Overte.Label { + width: parent.width + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + font.pixelSize: Overte.Theme.fontPixelSizeSmall + elide: Text.ElideRight + text: { + if ( + modelData.type === "Model" && + modelData.modelURL + ) { + return modelData.modelURL; + } else { + return modelData.type ?? "Unknown"; + } + } + } + } + + Overte.RoundButton { + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + + icon.source: "../icons/delete.svg" + icon.width: 24 + icon.height: 24 + + implicitWidth: 32 + implicitHeight: 32 + + horizontalPadding: 0 + verticalPadding: 0 + + onClicked: { + entityEditor.model.splice(index, 1); + + // force an update on the repeater, since + // JS arrays don't trigger change signals + entityEditor.modelChanged(); + } + } + } + } +} diff --git a/interface/resources/qml/overte/avatar_picker/MainPage.qml b/interface/resources/qml/overte/avatar_picker/MainPage.qml new file mode 100644 index 00000000000..30872f7e798 --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/MainPage.qml @@ -0,0 +1,233 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "." + +ColumnLayout { + RowLayout { + Image { + Layout.preferredWidth: 64 + Layout.preferredHeight: 64 + Layout.margins: 4 + id: currentIcon + + source: { + const meshURL = MyAvatar.skeletonModelURL; + let iconUrl = new URL(meshURL); + iconUrl.pathname = iconUrl.pathname.replace(/[.](?:fst|glb|fbx|vrm)$/i, ".jpg"); + return iconUrl; + } + + fillMode: Image.PreserveAspectFit + sourceSize.width: width + sourceSize.height: height + + // placeholder icon + Image { + anchors.fill: parent + + fillMode: Image.PreserveAspectFit + source: "../icons/avatars.png" + sourceSize.width: width + sourceSize.height: height + visible: currentIcon.status === Image.Error || currentIcon.status === Image.Null + } + } + + Overte.Label { + Layout.margins: 4 + Layout.fillWidth: true + id: currentName + + verticalAlignment: Text.AlignVCenter + text: { + const name = MyAvatar.getFullAvatarModelName(); + + if (name !== "") { + return name; + } else { + return qsTr("Unnamed"); + } + } + + Connections { + target: MyAvatar + + function onSkeletonModelURLChanged() { + const name = MyAvatar.getFullAvatarModelName(); + + if (name !== "") { + currentName.text = name; + } else { + currentName.text = qsTr("Unnamed"); + } + + console.info(name); + } + } + } + + Overte.RoundButton { + Layout.margins: 4 + + Overte.ToolTip { text: qsTr("Edit current") } + + icon.source: "../icons/pencil.svg" + icon.width: 32 + icon.height: 32 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: Overte.Theme.paletteActive.buttonInfo + + implicitWidth: 44 + implicitHeight: 44 + + onClicked: root.requestEdit(-1) + } + } + + RowLayout { + Layout.margins: 4 + + Overte.TextField { + Layout.fillWidth: true + + id: searchField + placeholderText: qsTr("Search…") + + Keys.onEnterPressed: { + searchButton.click(); + forceActiveFocus(); + } + Keys.onReturnPressed: { + searchButton.click(); + forceActiveFocus(); + } + } + + Overte.RoundButton { + id: searchButton + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + searchExpression = searchField.text === "" ? ".*" : searchField.text; + } + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + visible: root.availableTags.length !== 0 + + Overte.Label { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("Tags") + } + + ListView { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.fillWidth: true + implicitHeight: Overte.Theme.fontPixelSize * 2 + orientation: Qt.Horizontal + spacing: 2 + clip: true + + model: root.availableTags + delegate: Overte.Button { + required property int index + + implicitHeight: Overte.Theme.fontPixelSize * 2 + text: qsTr(ListView.view.model[index]) + checkable: true + checked: true + + palette.buttonText: checked ? Overte.Theme.paletteActive.highlightedText : Overte.Theme.paletteActive.button + backgroundColor: checked ? Overte.Theme.paletteActive.highlight : Overte.Theme.paletteActive.button + } + } + } + + GridView { + Layout.fillWidth: true + Layout.fillHeight: true + + // QT6TODO: remove this once mouse inputs work properly + interactive: false + + clip: true + // scales the cells to never leave dead space, but looks bad when scaling window + //cellWidth: (width - ScrollBar.vertical.width) / Math.floor(3 * (width / 480)) + cellWidth: Math.floor((480 - ScrollBar.vertical.width) / 3) + cellHeight: cellWidth + Overte.Theme.fontPixelSize + 6 + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + interactive: true + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + delegate: AvatarItem {} + + model: { + const searchRegex = new RegExp(searchExpression, "i"); + let tmp = []; + + for (const item of root.avatarModel) { + if (item.name.match(searchRegex)) { + let modelItem = item; + + if (!modelItem.iconUrl) { + modelItem.iconUrl = "../icons/avatars.svg"; + } + + if (!modelItem.tags) { modelItem.tags = []; } + + tmp.push(modelItem); + } + } + + return tmp; + } + } + + RowLayout { + Layout.margins: 4 + spacing: 8 + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + text: qsTr("%1 avatar(s)").arg(avatarModel.length) + } + + Overte.Label { + Layout.fillWidth: true + visible: editable + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + text: qsTr("Add new avatar") + } + + Overte.RoundButton { + icon.source: "../icons/plus.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: Overte.Theme.paletteActive.buttonAdd + implicitWidth: 48 + implicitHeight: 48 + visible: editable + + onClicked: addNewDialog.open() + } + } +} diff --git a/interface/resources/qml/overte/avatar_picker/qmldir b/interface/resources/qml/overte/avatar_picker/qmldir new file mode 100644 index 00000000000..1a6345b3c89 --- /dev/null +++ b/interface/resources/qml/overte/avatar_picker/qmldir @@ -0,0 +1,6 @@ +module AvatarPicker +AvatarPicker 1.0 AvatarPicker.qml +AvatarItem 1.0 AvatarItem.qml +MainPage 1.0 MainPage.qml +EditorPage 1.0 EditorPage.qml +EntityEditor 1.0 EntityEditor.qml diff --git a/interface/resources/qml/overte/chat/Chat.qml b/interface/resources/qml/overte/chat/Chat.qml new file mode 100644 index 00000000000..e12ec83ad76 --- /dev/null +++ b/interface/resources/qml/overte/chat/Chat.qml @@ -0,0 +1,129 @@ +import QtQuick +import QtQuick.Controls + +import "../" as Overte +import "." as OverteChat + +Rectangle { + id: root + anchors.fill: parent + color: Overte.Theme.paletteActive.base + + // stop the StackView from leaking out of overlay windows + clip: true + + property bool settingJoinNotifications: true + property bool settingBroadcast: false + property bool settingChatBubbles: true + property bool settingDesktopWindow: false + + property var typingIndicatorNames: ({}) + + // When the window gets closed, the chat history is forgotten. + // Keep a log of events we've received so we can replay them + // when recreating the window. + property list eventsLog: [] + + Component.onCompleted: { + const savedEvents = SettingsInterface.getValue("chat/eventsLog") ?? []; + for (let event of savedEvents) { + fromScript(event); + } + } + + onMessagesCleared: { + eventsLog = []; + toScript({event: "clear_history"}); + } + + // NOTE: "int" makes sense here as the timestamps are whole milliseconds, + // but it's 32 bits and overflows, so we need real's ~53 bits to work properly + signal messagePushed(name: string, body: string, timestamp: real) + signal notificationPushed(text: string, timestamp: real) + signal messagesCleared() + + function toScript(obj) { + sendToScript(JSON.stringify(obj)); + } + + function fromScript(rawObj) { + let obj = (typeof(rawObj) === "string") ? JSON.parse(rawObj) : rawObj; + const timestamp = obj.timestamp ?? Date.now(); + + switch (obj.event) { + case "recv_message": + messagePushed(obj.name ? obj.name : "", obj.body, timestamp); + break; + + case "user_joined": if (settingJoinNotifications) { + notificationPushed(qsTr("%1 joined").arg(obj.name), timestamp); + } break; + + case "user_left": if (settingJoinNotifications) { + notificationPushed(qsTr("%1 left").arg(obj.name), timestamp); + } break; + + case "user_name_changed": + notificationPushed( + qsTr("%1 changed their name to %2").arg(obj.old_name).arg(obj.new_name), + timestamp + ); + break; + + case "start_typing": + typingIndicatorNames[obj.uuid] = obj.name; + // propChanged is only fired by Qt on variable assignments, not property changes + typingIndicatorNamesChanged(); + break; + + case "end_typing": + delete typingIndicatorNames[obj.uuid]; + // propChanged is only fired by Qt on variable assignments, not property changes + typingIndicatorNamesChanged(); + break; + + case "change_setting": + updateSetting(obj.name, obj.value); + break; + + default: + console.error(`fromScript: Unknown event type "${obj.event}"`); + console.error(JSON.stringify(obj)); + break; + } + } + + function updateSetting(name, value) { + switch (name) { + case "join_notify": + settingJoinNotifications = value; + break; + + case "broadcast": + settingBroadcast = value; + break; + + case "chat_bubbles": + settingChatBubbles = value; + break; + + case "desktop_window": + settingDesktopWindow = value; + break; + } + } + + function sendSettingsUpdate() { + toScript({event: "change_setting", setting: "join_notify", value: settingJoinNotifications}); + toScript({event: "change_setting", setting: "broadcast", value: settingBroadcast}); + toScript({event: "change_setting", setting: "chat_bubbles", value: settingChatBubbles}); + toScript({event: "change_setting", setting: "desktop_window", value: settingDesktopWindow}); + } + + Overte.StackView { + anchors.fill: parent + + id: stack + initialItem: OverteChat.ChatPage {} + } +} diff --git a/interface/resources/qml/overte/chat/ChatPage.qml b/interface/resources/qml/overte/chat/ChatPage.qml new file mode 100644 index 00000000000..aa49ba0a27f --- /dev/null +++ b/interface/resources/qml/overte/chat/ChatPage.qml @@ -0,0 +1,273 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "../settings" as OverteSettings +import "." as OverteChat + +ColumnLayout { + id: chatPage + + RowLayout { + Layout.fillWidth: true + + // push everything to the right + Item { Layout.fillWidth: true } + + Overte.RoundButton { + Overte.ToolTip { text: qsTr("Clear chat history") } + + backgroundColor: ( + hovered ? + Overte.Theme.paletteActive.buttonDestructive : + Overte.Theme.paletteActive.button + ) + + implicitWidth: 36 + implicitHeight: 36 + horizontalPadding: 2 + verticalPadding: 2 + + icon.source: "../icons/delete.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: root.messagesCleared() + } + + Overte.RoundButton { + Overte.ToolTip { text: qsTr("Broadcast messages to whole domain") } + + backgroundColor: ( + checked ? + Overte.Theme.paletteActive.highlight : + Overte.Theme.paletteActive.button + ) + color: ( + checked ? + Overte.Theme.paletteActive.highlightedText : + Overte.Theme.paletteActive.buttonText + ) + + implicitWidth: 36 + implicitHeight: 36 + horizontalPadding: 2 + verticalPadding: 2 + + icon.source: "../icons/broadcast.svg" + icon.width: 24 + icon.height: 24 + icon.color: color + + checkable: true + checked: root.settingBroadcast + onToggled: { + root.settingBroadcast = checked; + root.sendSettingsUpdate(); + } + } + + Overte.RoundButton { + Overte.ToolTip { text: qsTr("Settings") } + + implicitWidth: 36 + implicitHeight: 36 + horizontalPadding: 2 + verticalPadding: 2 + + icon.source: "../icons/settings_cog.svg" + icon.width: 24 + icon.height: 24 + + onClicked: stack.push("./SettingsPage.qml") + } + } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + visible: chatLog.model.count === 0 + + id: noMessagesLabel + text: qsTr("No messages") + opacity: 0.5 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical: Overte.ScrollBar { + interactive: true + policy: ScrollBar.AlwaysOn + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + visible: chatLog.model.count > 0 + + ListView { + id: chatLog + clip: true + spacing: 8 + + model: ListModel {} + delegate: MessageBlock {} + + highlightMoveDuration: Overte.Theme.reducedMotion ? 1 : 500 + highlightResizeDuration: Overte.Theme.reducedMotion ? 1 : 500 + + Connections { + target: root + + function onMessagesCleared() { + chatLog.model.clear(); + } + + function onMessagePushed(name, body, timestamp) { + const imageUrlRegex = /(https?:\/\/\S+\.(?:png|jpg|jpeg|gif|webp|svg)\S*)/gmi; + let detectedImageUrls = body.match(imageUrlRegex) ?? []; + + chatLog.model.append({ + name: name, + body: ( + body + .replace(/\&/g, "&") + .replace(/\[/g, "[") + .replace(/\]/g, "]") + .replace(/\/g, ">") + .replace(/\'/g, "'") + .replace(/\"/g, """) + .replace(/\n/g, "
") + // strip any image links, as they're + // embedded and clickable anyway + .replace(imageUrlRegex, "") + ), + notification: "", + timestamp: timestamp, + imageEmbeds: detectedImageUrls.map(e => ({ modelData: e })), + }); + chatLog.currentIndex = chatLog.model.count - 1; + } + + function onNotificationPushed(text, timestamp) { + chatLog.model.append({ + name: "", + body: "", + notification: text, + timestamp: timestamp, + imageEmbeds: [], + }); + chatLog.currentIndex = chatLog.model.count - 1; + } + } + } + } + + Overte.Label { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + + id: typingIndicator + + text: "" + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + wrapMode: Text.Wrap + font.pixelSize: Overte.Theme.fontPixelSizeSmall + font.italic: true + + Connections { + target: root + + function onTypingIndicatorNamesChanged() { + const values = Object.values(root.typingIndicatorNames); + + if (values.length === 0) { + typingIndicator.text = ""; + } else { + typingIndicator.text = values.join(", ") + qsTr(" typing…"); + } + } + } + } + + RowLayout { + Layout.fillWidth: true + Layout.fillHeight: false + + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.maximumHeight: Overte.Theme.fontPixelSize * 6 + + ScrollBar.vertical: Overte.ScrollBar { + interactive: false + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + Overte.TextArea { + id: messageInput + placeholderText: ( + root.settingBroadcast ? + qsTr("Broadcast chat message…") : + qsTr("Local chat message…") + ) + wrapMode: TextEdit.Wrap + KeyNavigation.priority: KeyNavigation.BeforeItem + KeyNavigation.tab: messageSend + + Keys.onPressed: event => { + if ( + (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && + !(event.modifiers & Qt.ShiftModifier) + ) { + messageSend.clicked(); + event.accepted = true; + } + } + + property bool hadText: false + + onTextChanged: { + if (text === "") { + toScript({event: "end_typing"}); + hadText = false; + } else if (!hadText) { + toScript({event: "start_typing"}); + hadText = true; + } + } + } + } + + Overte.RoundButton { + id: messageSend + Layout.fillHeight: true + Layout.preferredWidth: 40 + horizontalPadding: 2 + verticalPadding: 2 + + icon.source: "../icons/send.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + let text = messageInput.text.trim(); + messageInput.text = ""; + + if (text === "") { return; } + + toScript({event: "send_message", body: text}); + } + } + } +} diff --git a/interface/resources/qml/overte/chat/MessageBlock.qml b/interface/resources/qml/overte/chat/MessageBlock.qml new file mode 100644 index 00000000000..c5bfdb0ab1c --- /dev/null +++ b/interface/resources/qml/overte/chat/MessageBlock.qml @@ -0,0 +1,171 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte + +ColumnLayout { + required property int index + required property string name + required property string body + required property string notification + required property real timestamp + required property var imageEmbeds + + id: messageBlock + + anchors.left: parent ? parent.left : undefined + anchors.right: parent ? parent.right : undefined + anchors.leftMargin: 4 + anchors.rightMargin: Overte.Theme.scrollbarWidth + + Rectangle { + Layout.fillWidth: true + implicitHeight: 3 + color: Overte.Theme.paletteActive.highlight + + ColorAnimation on color { + to: Overte.Theme.paletteActive.alternateBase + duration: 500 + } + } + + RowLayout { + Layout.leftMargin: 6 + Layout.rightMargin: 8 + Layout.fillWidth: true + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + + Overte.Label { + Layout.fillWidth: true + id: nameLabel + text: notification ? notification : name + font.pixelSize: Overte.Theme.fontPixelSizeSmall + font.bold: true + elide: Text.ElideRight + children: truncated ? [nameTooltipArea] : [] + + MouseArea { + id: nameTooltipArea + anchors.fill: parent + cursorShape: Qt.WhatsThisCursor + hoverEnabled: true + + Overte.ToolTip { + visible: parent.containsMouse + text: nameLabel.text + } + } + } + + Overte.Label { + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: (new Date(timestamp)).toLocaleTimeString(undefined, Locale.ShortFormat) + + MouseArea { + anchors.fill: parent + cursorShape: Qt.WhatsThisCursor + hoverEnabled: true + + Overte.ToolTip { + visible: parent.containsMouse + text: (new Date(timestamp)).toLocaleString(undefined, Locale.LongFormat) + } + } + } + } + + Overte.BodyText { + Layout.leftMargin: 6 + Layout.rightMargin: 16 + // If we used Layout.fillWidth, then the entire block width would be selectable. + // With this hack we only use the width we really need to + Layout.maximumWidth: parent.width - parent.anchors.leftMargin - parent.anchors.rightMargin + + visible: text.length > 0 + + // MD support is cool, but it'd only work properly in the QML chat + // app and not chat bubbles, where formatting isn't supported in Text entities. + //textFormat: TextEdit.MarkdownText + textFormat: TextEdit.RichText + + text: { + if (notification) { return ""; } + + return ( + body + .replace( + /(https?:\/\/\S+)/gi, + `$1` + ) + ); + } + } + + Repeater { + model: imageEmbeds + + Rectangle { + required property string modelData + + color: Overte.Theme.paletteActive.alternateBase + radius: Overte.Theme.borderRadius + + Layout.maximumWidth: messageBlock.width - 64 + Layout.maximumHeight: 320 + + implicitWidth: ( + embeddedImage.status === Image.Ready ? + embeddedImage.sourceSize.width + (Overte.Theme.borderWidth * 2) : + messageBlock.width - 64 + ) + implicitHeight: Math.max( + Overte.Theme.fontPixelSize * 2, + embeddedImage.sourceSize.width + (Overte.Theme.borderWidth * 2) + ) + + AnimatedImage { + anchors.fill: parent + anchors.margins: Overte.Theme.borderWidth + + id: embeddedImage + fillMode: Image.PreserveAspectFit + autoTransform: true + source: modelData + //sourceSize.width: width + //sourceSize.height: height + + MouseArea { + anchors.fill: parent + id: embedMouseArea + + hoverEnabled: true + propagateComposedEvents: true + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton + + onClicked: Qt.openUrlExternally(modelData) + } + } + + Overte.ToolTip { + text: modelData + visible: embedMouseArea.containsMouse + } + + Overte.Label { + visible: embeddedImage.status !== Image.Ready + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: ( + embeddedImage.status === Image.Loading ? + qsTr("Loading image… %1%%").arg(Math.floor(embeddedImage.progress * 100)) : + qsTr("Error loading image") + ) + } + } + } +} diff --git a/interface/resources/qml/overte/chat/SettingsPage.qml b/interface/resources/qml/overte/chat/SettingsPage.qml new file mode 100644 index 00000000000..b014635307a --- /dev/null +++ b/interface/resources/qml/overte/chat/SettingsPage.qml @@ -0,0 +1,71 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "../settings" as OverteSettings + +Column { + id: settingsPage + spacing: 16 + + Item { + anchors.left: parent.left + anchors.right: parent.right + implicitHeight: Math.max(settingsBackBtn.implicitHeight, settingsTitleLabel.implicitHeight) + + Overte.Label { + anchors.fill: parent + + id: settingsTitleLabel + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Settings - Chat") + } + + Overte.Button { + id: settingsBackBtn + icon.source: "../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + text: qsTr("Back", "Return to previous page") + + onClicked: { + stack.pop(); + } + } + } + + OverteSettings.SwitchSetting { + text: qsTr("Join/Leave Notifications") + + value: root.settingJoinNotifications + onValueChanged: { + root.settingJoinNotifications = value; + root.sendSettingsUpdate(); + } + } + + OverteSettings.SwitchSetting { + text: qsTr("Chat Bubbles") + + value: root.settingChatBubbles + onValueChanged: { + root.settingChatBubbles = value; + root.sendSettingsUpdate(); + } + } + + OverteSettings.SwitchSetting { + text: qsTr("Desktop Window") + + value: root.settingDesktopWindow + onValueChanged: { + root.settingDesktopWindow = value; + root.sendSettingsUpdate(); + } + } +} diff --git a/interface/resources/qml/overte/compat/CompatAssetDialog.qml b/interface/resources/qml/overte/compat/CompatAssetDialog.qml new file mode 100644 index 00000000000..addf54b4102 --- /dev/null +++ b/interface/resources/qml/overte/compat/CompatAssetDialog.qml @@ -0,0 +1,33 @@ +import QtCore +import QtQuick + +import ".." as Overte +import "../dialogs" as OverteDialogs + +import "../../windows" as HifiWindows + +// compatibility shim with Hifi FileDialog +HifiWindows.Window { + id: root + resizable: true + implicitWidth: 480 + implicitHeight: 480 + minSize: Qt.vector2d(480, 360) + destroyOnHidden: true + objectName: "AssetServer" + title: qsTr("Asset Browser") + opacity: parent.opacity + + Settings { + category: "Overlay.AssetServer" + property alias width: root.width + property alias height: root.height + property alias x: root.x + property alias y: root.y + } + + OverteDialogs.AssetDialog { + anchors.fill: parent + id: assetDialog + } +} diff --git a/interface/resources/qml/overte/compat/CompatFileDialog.qml b/interface/resources/qml/overte/compat/CompatFileDialog.qml new file mode 100644 index 00000000000..3d93c1feec1 --- /dev/null +++ b/interface/resources/qml/overte/compat/CompatFileDialog.qml @@ -0,0 +1,72 @@ +import QtCore +import QtQuick + +import ".." as Overte +import "../dialogs" as OverteDialogs + +import "../../windows" as HifiWindows + +// compatibility shim with Hifi FileDialog +HifiWindows.Window { + id: root + resizable: true + implicitWidth: 480 + implicitHeight: 360 + minSize: Qt.vector2d(360, 240) + destroyOnCloseButton: true + destroyOnHidden: true + modality: Qt.ApplicationModal + + Settings { + category: "FileDialog" + property alias width: root.width + property alias height: root.height + property alias x: root.x + property alias y: root.y + } + + property alias caption: root.title + property string dir: "" + property var filter // maybe not necessary? + + property bool selectDirectory: false + property bool multiSelect: false // unused + property bool saveDialog: false + + property var options + + signal selectedFile(var file) + signal canceled + + onSelectedFile: root.destroy() + onCanceled: root.destroy() + onWindowClosed: canceled() + + Component.onCompleted: { + if (dir !== "") { + fileDialog.currentFolder = dir; + } + } + + OverteDialogs.FileDialog { + anchors.fill: parent + + id: fileDialog + visible: true + + fileMode: { + if (root.selectDirectory) { + return OverteDialogs.FileDialog.FileMode.OpenFolder; + } else if (root.saveDialog) { + return OverteDialogs.FileDialog.FileMode.SaveFile; + } else if (root.multiSelect) { + return OverteDialogs.FileDialog.FileMode.OpenFiles; + } else { + return OverteDialogs.FileDialog.FileMode.OpenFile; + } + } + + onAccepted: root.selectedFile(fileDialog.selectedFile) + onRejected: root.canceled() + } +} diff --git a/interface/resources/qml/overte/compat/RunningScripts_Window.qml b/interface/resources/qml/overte/compat/RunningScripts_Window.qml new file mode 100644 index 00000000000..b5a45d86ccc --- /dev/null +++ b/interface/resources/qml/overte/compat/RunningScripts_Window.qml @@ -0,0 +1,14 @@ +import ".." as Overte +import "../dialogs" as OverteDialogs + +import "../../windows" as HifiWindows + +HifiWindows.Window { + objectName: "RunningScripts" + title: qsTr("Running Scripts") + opacity: parent.opacity + resizable: true + minSize: Qt.vector2d(384, 192) + + OverteDialogs.RunningScriptsDialog {} +} diff --git a/interface/resources/qml/overte/compat/qmldir b/interface/resources/qml/overte/compat/qmldir new file mode 100644 index 00000000000..66496dbc57d --- /dev/null +++ b/interface/resources/qml/overte/compat/qmldir @@ -0,0 +1,4 @@ +module Compat +RunningScripts_Window 1.0 RunningScripts_Window.qml +CompatFileDialog 1.0 CompatFileDialog.qml +CompatAssetDialog 1.0 CompatAssetDialog.qml diff --git a/interface/resources/qml/overte/contacts/AccountAvatar.qml b/interface/resources/qml/overte/contacts/AccountAvatar.qml new file mode 100644 index 00000000000..b1530d75937 --- /dev/null +++ b/interface/resources/qml/overte/contacts/AccountAvatar.qml @@ -0,0 +1,59 @@ +import QtQuick +import QtQuick.Effects + +import ".." as Overte + +Rectangle { + required property int status + property alias source: avatarImage.source + property alias retainWhileLoading: avatarImage.retainWhileLoading + + color: "transparent" + + implicitWidth: 64 + border.width + implicitHeight: 64 + border.width + + id: avatar + radius: Overte.Theme.borderRadius + border.width: Math.max(2, Overte.Theme.borderWidth) + border.color: { + switch (status) { + case -1: return Overte.Theme.paletteActive.statusOffline; + case 0: return Overte.Theme.paletteActive.statusOffline; + case 1: return Overte.Theme.paletteActive.statusFriendsOnly; + case 2: return Overte.Theme.paletteActive.statusContacts; + case 3: return Overte.Theme.paletteActive.statusEveryone; + } + } + + Image { + anchors.fill: avatar + anchors.margins: avatar.border.width + fillMode: Image.PreserveAspectCrop + + id: avatarImage + sourceSize.width: width + sourceSize.height: height + + layer.enabled: true + layer.effect: MultiEffect { + anchors.fill: avatarImage + source: avatarImage + maskEnabled: true + maskSource: mask + } + } + + Item { + id: mask + anchors.fill: avatar + visible: false + + layer.enabled: true + Rectangle { + anchors.fill: parent + radius: avatar.radius + color: "black" + } + } +} diff --git a/interface/resources/qml/overte/contacts/AccountContact.qml b/interface/resources/qml/overte/contacts/AccountContact.qml new file mode 100644 index 00000000000..fce5036b66d --- /dev/null +++ b/interface/resources/qml/overte/contacts/AccountContact.qml @@ -0,0 +1,111 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import ".." as Overte +import "." + +Rectangle { + id: control + color: index % 2 === 0 ? Overte.Theme.paletteActive.base : Overte.Theme.paletteActive.alternateBase + implicitWidth: ListView.view.contentWidth + implicitHeight: 96 + + required property int index + + // directory username + required property string user + required property url avatarUrl + required property int status + required property bool friend + required property string currentPlaceName + + ColumnLayout { + anchors.fill: parent + anchors.margins: 4 + anchors.leftMargin: 8 + anchors.rightMargin: 8 + + RowLayout { + Layout.fillWidth: true + spacing: 16 + + AccountAvatar { + source: avatarUrl + status: control.status + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + text: user + } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + verticalAlignment: Text.AlignVCenter + + color: { + switch (control.status) { + case 0: return Overte.Theme.paletteActive.text; + case 1: return Overte.Theme.paletteActive.statusFriendsOnly; + case 2: return Overte.Theme.paletteActive.statusContacts; + case 3: return Overte.Theme.paletteActive.statusEveryone; + } + } + + text: { + switch (control.status) { + case 0: return qsTr("Offline"); + case 1: return qsTr("Friends Only"); + case 2: return qsTr("Contacts") + case 3: return qsTr("Everyone") + } + } + } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.8 + verticalAlignment: Text.AlignVCenter + text: currentPlaceName + visible: currentPlaceName !== "" + } + } + + Overte.RoundButton { + // TODO + visible: false + enabled: false + + backgroundColor: ( + control.friend ? + Overte.Theme.paletteActive.buttonDestructive : + Overte.Theme.paletteActive.buttonAdd + ) + icon.source: ( + control.friend ? + "../icons/remove_friend.svg" : + "../icons/add_friend.svg" + ) + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + // TODO + control.friend = !control.friend; + } + } + } + } +} diff --git a/interface/resources/qml/overte/contacts/ContactsList.qml b/interface/resources/qml/overte/contacts/ContactsList.qml new file mode 100644 index 00000000000..aa9de3aed6f --- /dev/null +++ b/interface/resources/qml/overte/contacts/ContactsList.qml @@ -0,0 +1,431 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import ".." as Overte +import "." + +Rectangle { + id: contactsList + anchors.fill: parent + implicitWidth: 480 + implicitHeight: 720 + color: Overte.Theme.paletteActive.base + + property string localSearchExpression: ".*" + property string accountSearchExpression: ".*" + + property list localContactsModel: [] + property list accountContactsModel: [] + property var adminData: ({}) + + property var waitingRequestCallbacks: ({}) + + function fromScript(message) { + const data = JSON.parse(message); + + switch (data.action) { + case "system:auth_request": { + if (contactsList.waitingRequestCallbacks[data.data.cookie]) { + contactsList.waitingRequestCallbacks[data.data.cookie](data.data); + delete contactsList.waitingRequestCallbacks[data.data.cookie]; + } + } break; + } + } + + function authRequest(data) { + let cookie = Date.now() + Math.floor(Math.random() * (1000 - -1000) + -1000); + + contactsList.waitingRequestCallbacks[cookie] = data.callback; + + sendToScript(JSON.stringify({ + action: "system:auth_request", + data: { + method: data.method ?? "GET", + url: data.url, + body: data.body, + cookie: cookie, + } + })); + } + + function updateLocalContacts() { + const palData = AvatarManager.getPalData().data; + let tmp = []; + + for (const data of palData) { + // don't add ourselves to the contacts list + if (!data.sessionUUID) { continue; } + + if (!contactsList.adminData[data.sessionUUID]) { + Users.requestUsernameFromID(data.sessionUUID); + } + + tmp.push({ + uuid: data.sessionUUID, + name: data.sessionDisplayName ? data.sessionDisplayName : `${qsTr("Unnamed")} ${data.sessionUUID}`, + volume: Users.getAvatarGain(data.sessionUUID), + }); + } + + localContactsModel = tmp; + } + + function updateAccountContactsImpl(accounts) { + console.debug("Contacts list data received"); + let tmp = []; + + for (const entry of accounts) { + // FIXME: /api/v1/users/connections doesn't give you the availability + // of a connection, only whether they're on a server or not + let status = entry.location?.online ? 2 : 0; + + let img = entry.images?.tiny ?? entry.images?.thumbnail; + + // some accounts have a bugged default avatar that doesn't exist + if ( + img === "assets/brand-icon-256.png" || + !(img && img.match(/^https?:\/\//)) + ) { + img = undefined; + } + + tmp.push({ + user: entry.username, + avatarUrl: img ?? "../icons/unset_avatar.svg", + status: status, + currentPlaceName: "", + friend: entry.connection === "friend", + }); + } + + tmp.sort((a, b) => ( + ((a.status !== 0 ? 1 : 0) - (b.status !== 0 ? 1 : 0)) || + a.user.localeCompare(b.user) + )); + + accountContactsModel = tmp; + } + + function updateAccountContacts() { + if (!AccountServices.loggedIn) { + accountContactsModel = []; + return; + } + + console.debug("Requesting contacts list…"); + authRequest({ + method: "GET", + url: `${AccountServices.metaverseServerURL}/api/v1/users/connections`, + callback: response => { + try { + let data = JSON.parse(response.responseText); + if (data.status === "success" && data?.data?.users) { + updateAccountContactsImpl(data.data.users); + } else { + console.error("Failed to get contacts list"); + console.debug(response.responseText); + } + } catch (e) { + console.error(e); + } + }, + }); + } + + Connections { + target: Users + + function usernameFromIDReply(sessionUUID, username, _fingerprint, isAdmin) { + contactsList.adminData[sessionUUID] = { + username: username, + badge: isAdmin ? "../icons/admin_shield.svg" : "" + }; + + // only assignments are checked, not property changes, so force the update signal + contactsList.adminDataChanged(); + } + + function avatarRemovedEvent(sessionUUID) { + delete contactsList.adminData[sessionUUID]; + updateLocalContacts(); + } + + function avatarAddedEvent(sessionUUID) { + updateLocalContacts(); + } + } + + Connections { + target: AccountServices + + function loggedInChanged(_loggedIn) { + updateAccountContacts(); + } + } + + Component.onCompleted: { + updateLocalContacts(); + updateAccountContacts(); + } + + ColumnLayout { + anchors.fill: contactsList + + MyAccountInfo { + Layout.fillWidth: true + id: myAccountInfo + } + + Overte.TabBar { + Layout.fillWidth: true + id: tabBar + + Overte.TabButton { text: qsTr("Local") } + Overte.TabButton { text: qsTr("Account") } + } + + StackLayout { + currentIndex: tabBar.currentIndex + + // Local + ColumnLayout { + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + + visible: !localContactsList.visible + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + text: qsTr("It's just you here") + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + + id: localContactsList + visible: model.length > 0 + clip: true + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + } + + contentWidth: contactsList.width - Overte.Theme.scrollbarWidth + + model: { + const regex = new RegExp(localSearchExpression, "i"); + let tmp = []; + + for (const item of localContactsModel) { + if (item.name.match(regex) || item.user.match(regex)) { + tmp.push(item); + } + } + + return tmp; + } + delegate: SessionContact {} + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 4 + Layout.rightMargin: 4 + + Overte.TextField { + Layout.fillWidth: true + + Keys.onEnterPressed: { + localSearchButton.clicked(); + forceActiveFocus(); + } + + Keys.onReturnPressed: { + localSearchButton.clicked(); + forceActiveFocus(); + } + + placeholderText: qsTr("Search…") + id: localSearchField + } + + Overte.RoundButton { + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + id: localSearchButton + + onClicked: localSearchExpression = localSearchField.text + } + } + } + + // Account + ColumnLayout { + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + visible: myAccountInfo.status !== MyAccountInfo.Status.LoggedOut + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + } + + contentWidth: contactsList.width - Overte.Theme.scrollbarWidth + + model: { + const regex = new RegExp(accountSearchExpression, "i"); + let tmp = []; + + for (const item of accountContactsModel) { + if (item.user.match(regex)) { + tmp.push(item); + } + } + + return tmp; + } + delegate: AccountContact {} + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 4 + Layout.rightMargin: 4 + visible: myAccountInfo.status !== MyAccountInfo.Status.LoggedOut + + Overte.TextField { + Layout.fillWidth: true + + Keys.onEnterPressed: { + accountSearchButton.clicked(); + forceActiveFocus(); + } + + Keys.onReturnPressed: { + accountSearchButton.clicked(); + forceActiveFocus(); + } + + placeholderText: qsTr("Search…") + id: accountSearchField + } + + Overte.RoundButton { + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + id: accountSearchButton + + onClicked: accountSearchExpression = accountSearchField.text + } + } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + visible: myAccountInfo.status === MyAccountInfo.Status.LoggedOut + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Log in to track contacts") + } + } + } + } + + Overte.Dialog { + id: changeAvatarDialog + anchors.fill: parent + maxWidth: -1 + + signal accepted + signal rejected + + onAccepted: { + myAccountInfo.avatarUrl = avatarChangeUrlField.text; + + authRequest({ + method: "POST", + url: `${AccountServices.metaverseServerURL}/api/v1/account/${AccountServices.username}/field/images_tiny`, + body: JSON.stringify({ set: myAccountInfo.avatarUrl }), + }); + + avatarChangeUrlField.text = ""; + close(); + } + + onRejected: { + avatarChangeUrlField.text = ""; + close(); + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Change profile picture") + } + Overte.Ruler { Layout.fillWidth: true } + + Overte.Label { + Layout.topMargin: 12 + Layout.bottomMargin: 12 + Layout.fillWidth: true + wrapMode: Text.Wrap + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: qsTr("Profile pictures aren't stored on the directory server, so you need to supply a URL to your profile picture.\n\nSome services that let you upload images will give you links that will expire and stop working after a while.\n\n100x100 JPEG or WebP is recommended.") + } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: qsTr("Profile picture URL") + id: avatarChangeUrlField + } + + RowLayout { + Layout.preferredWidth: 720 + Layout.fillWidth: true + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + text: qsTr("Cancel") + + onClicked: changeAvatarDialog.rejected(); + } + + Item { + Layout.preferredWidth: 1 + Layout.fillWidth: true + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Apply") + enabled: ( + avatarChangeUrlField.text !== "" && + avatarChangeUrlField.text.match(/^https?:\/\//) + ) + + onClicked: changeAvatarDialog.accepted() + } + } + } + } +} diff --git a/interface/resources/qml/overte/contacts/MyAccountInfo.qml b/interface/resources/qml/overte/contacts/MyAccountInfo.qml new file mode 100644 index 00000000000..ded4de11954 --- /dev/null +++ b/interface/resources/qml/overte/contacts/MyAccountInfo.qml @@ -0,0 +1,213 @@ +import QtCore as QtCore +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import ".." as Overte +import "." + +Rectangle { + id: root + radius: 8 + border.width: Overte.Theme.borderWidth + border.color: Qt.darker(color, 1.2) + color: Overte.Theme.paletteActive.base + + implicitHeight: 96 + implicitWidth: 320 + + enum Status { + LoggedOut = -1, + Invisible, + FriendsOnly, + Contacts, + Everyone + } + + // cache the last avatar image url so there's not a few + // seconds of placeholder avatar while the profile loads + QtCore.Settings { + id: settings + category: "MyAccountInfo" + property url cachedAvatarUrl: "../icons/unset_avatar.svg" + } + + Connections { + target: AccountServices + + function loggedInChanged(loggedIn) { + updateStatus(); + } + } + + Component.onCompleted: updateStatus() + + function updateStatus() { + if (status === MyAccountInfo.Status.LoggedOut && AccountServices.loggedIn) { + contactsList.authRequest({ + url: `${AccountServices.metaverseServerURL}/api/v1/account/${AccountServices.username}/field/images_tiny`, + callback: response => { + const data = JSON.parse(response.responseText); + if (data.data) { + root.avatarUrl = data.data; + } + }, + }); + } + + if (!AccountServices.loggedIn) { + status = MyAccountInfo.Status.LoggedOut; + } else { + switch (AccountServices.findableBy) { + case "none": status = MyAccountInfo.Status.Invisible; break; + case "friends": status = MyAccountInfo.Status.FriendsOnly; break; + case "contacts": status = MyAccountInfo.Status.Contacts; break; + case "all": status = MyAccountInfo.Status.Everyone; break; + } + } + } + + property int status: MyAccountInfo.Status.LoggedOut + + property string avatarUrl: settings.cachedAvatarUrl + + onAvatarUrlChanged: settings.cachedAvatarUrl = avatarUrl + + readonly property color currentStatusColor: { + switch (status) { + case MyAccountInfo.Status.LoggedOut: + return Overte.Theme.paletteActive.statusOffline; + + case MyAccountInfo.Status.Invisible: + return Overte.Theme.paletteActive.statusOffline; + + case MyAccountInfo.Status.FriendsOnly: + return Overte.Theme.paletteActive.statusFriendsOnly; + + case MyAccountInfo.Status.Contacts: + return Overte.Theme.paletteActive.statusContacts; + + case MyAccountInfo.Status.Everyone: + return Overte.Theme.paletteActive.statusEveryone; + } + } + + readonly property string currentStatusName: { + switch (status) { + case MyAccountInfo.Status.LoggedOut: + return qsTr("Logged Out"); + + case MyAccountInfo.Status.Invisible: + return qsTr("Invisible"); + + case MyAccountInfo.Status.FriendsOnly: + return qsTr("Friends Only"); + + case MyAccountInfo.Status.Contacts: + return qsTr("Contacts"); + + case MyAccountInfo.Status.Everyone: + return qsTr("Everyone"); + } + } + + RowLayout { + anchors.fill: parent + + AccountAvatar { + id: avatarImg + source: ( + root.status !== MyAccountInfo.Status.LoggedOut ? + root.avatarUrl : + "../icons/unset_avatar.svg" + ) + status: root.status + retainWhileLoading: true + Layout.preferredWidth: 64 + Layout.preferredHeight: 64 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + + Overte.Button { + anchors.fill: parent + anchors.margins: Overte.Theme.borderWidth + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + opacity: hovered ? (Overte.Theme.highContrast ? 1.0 : 0.7) : 0 + enabled: AccountServices.loggedIn + visible: AccountServices.loggedIn + + icon.source: "../icons/plus.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: changeAvatarDialog.open() + + Overte.ToolTip { text: qsTr("Change profile picture") } + } + } + + ColumnLayout { + Layout.rightMargin: 8 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + + Overte.TextField { + Layout.fillWidth: true + verticalAlignment: Text.AlignVCenter + + id: displayName + text: MyAvatar.displayName + placeholderText: AccountServices.username + + onEditingFinished: MyAvatar.displayName = text + } + + RowLayout { + Layout.fillWidth: true + + Overte.Label { + text: root.status === MyAccountInfo.Status.Invisible ? "â—‹" : "â—" + color: root.currentStatusColor + } + + Overte.ComboBox { + Layout.fillWidth: true + + id: statusBox + flat: true + color: root.status === MyAccountInfo.Status.Invisible ? Overte.Theme.paletteActive.windowText : root.currentStatusColor + textRole: "text" + valueRole: "value" + visible: root.status !== MyAccountInfo.Status.LoggedOut + + onActivated: index => { + root.status = index; + + switch (index) { + case 0: AccountServices.findableBy = "none"; break; + case 1: AccountServices.findableBy = "friends"; break; + case 2: AccountServices.findableBy = "connections"; break; + case 3: AccountServices.findableBy = "all"; break; + } + } + + currentIndex: root.status + model: [ + { value: MyAccountInfo.Status.Invisible, text: qsTr("Invisible") }, + { value: MyAccountInfo.Status.FriendsOnly, text: qsTr("Friends Only") }, + { value: MyAccountInfo.Status.Contacts, text: qsTr("Contacts") }, + { value: MyAccountInfo.Status.Everyone, text: qsTr("Everyone") }, + ] + } + + Overte.Label { + Layout.fillWidth: true + visible: root.status === MyAccountInfo.Status.LoggedOut + verticalAlignment: Text.AlignVCenter + text: qsTr("Logged Out") + } + } + } + } +} diff --git a/interface/resources/qml/overte/contacts/SessionContact.qml b/interface/resources/qml/overte/contacts/SessionContact.qml new file mode 100644 index 00000000000..e0c37949f66 --- /dev/null +++ b/interface/resources/qml/overte/contacts/SessionContact.qml @@ -0,0 +1,114 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import ".." as Overte + +Rectangle { + id: control + color: index % 2 === 0 ? Overte.Theme.paletteActive.base : Overte.Theme.paletteActive.alternateBase + implicitWidth: ListView.view.contentWidth + implicitHeight: 96 + + required property int index + + required property string uuid + required property string name + required property real volume + + readonly property string username: contactsList.adminData[uuid]?.username ?? "" + readonly property url badgeIconSource: contactsList.adminData[uuid]?.badge ?? "" + + // TODO: does this work? + function dbToReal(x) { + return 1.0 - (x / 100); + } + + function realToDb(x) { + return (1.0 - x) * 100; + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 4 + anchors.leftMargin: 8 + anchors.rightMargin: 8 + + RowLayout { + Layout.fillWidth: true + spacing: 16 + + Image { + Layout.leftMargin: 6 + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + source: badgeIconSource + } + + ColumnLayout { + Layout.fillWidth: true + + Overte.Label { + Layout.fillWidth: true + elide: Text.ElideRight + text: name + } + + Overte.BodyText { + font.pixelSize: Overte.Theme.fontPixelSizeSmall + visible: username !== "" + //elide: Text.ElideRight + text: username + palette.buttonText: Overte.Theme.paletteActive.link + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + } + } + + Item { Layout.fillWidth: true } + + // TODO: find a way of disabling this if the target is already in the account + // contacts, might be tricky with how usernames are hidden from non-admins + /*Overte.RoundButton { + icon.source: "../icons/add_friend.svg" + icon.width: 24 + icon.height: 24 + + backgroundColor: hovered ? Overte.Theme.paletteActive.buttonAdd : Overte.Theme.paletteActive.button + + Overte.ToolTip { text: qsTr("Send contact request") } + + onClicked: console.warn("TODO") + }*/ + } + + RowLayout { + Overte.RoundButton { + icon.source: volumeSlider.value === 0.0 ? "../icons/speaker_muted.svg" : "../icons/speaker_active.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: volumeSlider.value = 0.0 + } + + Overte.Slider { + Layout.fillWidth: true + id: volumeSlider + from: 0.0 + to: 1.2 + stepSize: 0.1 + snapMode: Slider.SnapAlways + value: dbToReal(volume) + + onMoved: Users.setAvatarGain(control.uuid, realToDb(value)) + } + + Overte.Label { + Layout.preferredWidth: Overte.Theme.fontPixelSize * 3 + //text: `${Math.round(volumeSlider.value * 100)}%` + // why did hifi have to use decibels for avatar volume?? + text: `${Math.round(volumeSlider.value * 100)}%\n${volume} db` + } + } + } +} diff --git a/interface/resources/qml/overte/contacts/qmldir b/interface/resources/qml/overte/contacts/qmldir new file mode 100644 index 00000000000..f32fee462df --- /dev/null +++ b/interface/resources/qml/overte/contacts/qmldir @@ -0,0 +1,6 @@ +module Contacts +ContactsList 1.0 ContactsList.qml +AccountAvatar 1.0 AccountAvatar.qml +SessionContact 1.0 SessionContact.qml +AccountContact 1.0 AccountContact.qml +MyAccountInfo 1.0 MyAccountInfo.qml diff --git a/interface/resources/qml/overte/dialogs/AssetDialog.qml b/interface/resources/qml/overte/dialogs/AssetDialog.qml new file mode 100644 index 00000000000..5fda22bdceb --- /dev/null +++ b/interface/resources/qml/overte/dialogs/AssetDialog.qml @@ -0,0 +1,384 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.folderlistmodel +import Qt.labs.qmlmodels + +import ".." as Overte + +Rectangle { + id: dialog + color: Overte.Theme.paletteActive.base + + property url selectedFile: "" + property string searchExpression: ".*" + + // FIXME: "Could not convert argument 0 from [object Object] to EntityItemProperties" + //readonly property var spawnableRegex: /\.(gltf|glb|vrm|fbx|fst|obj|png|jpeg|jpg|webp)$/i + readonly property var spawnableRegex: /\.(gltf|glb|vrm|fbx|fst|obj)$/i + + property var assetMappingModel: ({}) + + Component.onCompleted: refreshModel() + + function refreshModel() { + Assets.getAllMappings((error, maps) => { + if (error === "") { + assetMappingModel = maps; + tableModel.rows = getDirectoryModel(); + } else { + console.error(error); + } + }); + } + + // this seems suboptimal but i can't figure out how else to use the Assets models + function getDirectoryModel() { + let tmp = []; + + for (const [name, hash] of Object.entries(assetMappingModel)) { + if (!name.slice(1).match(new RegExp(searchExpression, "i"))) { + continue; + } + + tmp.push({ + // trim off the leading slash + name: name.slice(1), + path: name, + hash: hash, + }); + } + + tmp.sort((a, b) => a.name.localeCompare(b.name)); + + return tmp; + } + + component TableDelegate: Rectangle { + required property TableView tableView + required property int row + required property bool current + + property string name: tableModel.rows[row]?.name ?? "undefined" + + implicitWidth: Math.max(tableView.contentWidth, label.implicitWidth) + implicitHeight: Overte.Theme.fontPixelSize * 2 + + color: { + if (current) { + return Overte.Theme.paletteActive.highlight; + } else if (row % 2 === 0) { + return Overte.Theme.paletteActive.base; + } else { + return Overte.Theme.paletteActive.alternateBase; + } + } + + Overte.Label { + id: label + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.leftMargin: 4 + verticalAlignment: Text.AlignVCenter + color: current ? Overte.Theme.paletteActive.highlightedText : Overte.Theme.paletteActive.buttonText + text: name + } + } + + ColumnLayout { + anchors.fill: parent + + RowLayout { + Layout.fillWidth: true + Layout.margins: 4 + + Overte.Button { + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Upload File") + + // TODO + enabled: false + onClicked: {} + } + + Overte.TextField { + Layout.fillWidth: true + Layout.preferredHeight: parent.height + + id: searchField + placeholderText: qsTr("Search…") + + Keys.onEnterPressed: { + searchButton.click(); + searchField.forceActiveFocus(); + } + + Keys.onReturnPressed: { + searchButton.click(); + searchField.forceActiveFocus(); + } + } + + Overte.RoundButton { + id: searchButton + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + searchExpression = searchField.text === "" ? ".*" : searchField.text; + + // refresh the listing model + tableModel.rows = getDirectoryModel(); + } + } + } + + TableView { + Layout.fillWidth: true + Layout.fillHeight: true + + id: tableView + clip: true + selectionBehavior: TableView.SelectRows + selectionMode: TableView.SingleSelection + pixelAligned: true + rowSpacing: 1 + columnSpacing: 0 + boundsBehavior: Flickable.StopAtBounds + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AlwaysOn + interactive: true + } + + ScrollBar.horizontal: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + interactive: true + } + + contentWidth: width - ScrollBar.vertical.width + + model: TableModel { + id: tableModel + + TableModelColumn { display: "name" } + + rows: [] + } + + selectionModel: ItemSelectionModel {} + delegate: TableDelegate {} + } + + GridLayout { + rows: 2 + columns: 2 + Layout.fillWidth: true + Layout.margins: 8 + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + // TODO: might work, don't have a way of testing atm + //enabled: tableView.currentRow !== -1 + enabled: false + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + text: qsTr("Delete") + + onClicked: { + const data = tableModel.rows[tableView.currentRow]; + deleteWarningDialog.entryToDelete = data.name; + deleteWarningDialog.open(); + } + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + enabled: tableView.currentRow !== -1 + backgroundColor: Overte.Theme.paletteActive.buttonInfo + text: qsTr("Copy Link") + + onClicked: { + const data = tableModel.rows[tableView.currentRow]; + WindowScriptingInterface.copyToClipboard(`atp:${data.path}`); + } + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + // TODO: might work, don't have a way of testing atm + // enabled: tableView.currentRow !== -1 + enabled: false + text: qsTr("Rename") + + onClicked: { + const data = tableModel.rows[tableView.currentRow]; + renameDialog.entryToRename = data.name; + renameDialog.open(); + } + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + enabled: { + if (tableView.currentRow === -1) { return false; } + return tableModel.rows[tableView.currentRow].name.match(spawnableRegex); + } + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Create Entity") + + onClicked: { + const data = tableModel.rows[tableView.currentRow]; + spawnEntity(data.path); + } + } + } + } + + Overte.MessageDialog { + id: deleteWarningDialog + anchors.fill: parent + + property string entryToDelete: "" + text: qsTr("Are you sure you want to delete %1?").arg(entryToDelete) + + onAccepted: { + // put the leading slash back on + Assets.deleteMapping(`/${entryToDelete}`); + entryToDelete = ""; + close(); + } + + onRejected: { + entryToDelete = ""; + close(); + } + } + + Overte.Dialog { + id: renameDialog + anchors.fill: parent + + property string entryToRename: "" + + signal accepted + signal rejected + + onAccepted: { + // put the leading slash back on + Assets.renameMapping(`/${entryToRename}`, `/${assetRenameField.text}`, refreshModel); + assetRenameField.text = ""; + entryToRename = ""; + close(); + } + + onRejected: { + assetRenameField.text = ""; + entryToRename = ""; + close(); + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Rename asset") + } + Overte.Ruler { Layout.fillWidth: true } + + Overte.TextField { + Layout.preferredWidth: 720 + Layout.fillWidth: true + placeholderText: qsTr("New asset name") + text: renameDialog.entryToRename + id: assetRenameField + } + + RowLayout { + Overte.Button { + Layout.minimumWidth: 128 + Layout.fillWidth: true + text: qsTr("Cancel") + + onClicked: renameDialog.rejected() + } + + Item { Layout.fillWidth: true } + + Overte.Button { + Layout.minimumWidth: 128 + Layout.fillWidth: true + + enabled: assetRenameField.text !== "" + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Rename") + + onClicked: renameDialog.accepted() + } + } + } + } + + function spawnEntity(path) { + let isImage = path.match(/\.(png|jpeg|jpg|webp)$/i); + let isMesh = path.match(/\.(gltf|glb|vrm|fbx|fst|obj)$/i); + + // FIXME: "Could not convert argument 0 from [object Object] to EntityItemProperties" + if (isImage) { + /*Entities.addEntity({ + type: "Image", + imageURL: `atp:${path}`, + emissive: true, + keepAspectRatio: true, + rotation: MyAvatar.orientation, + position: Vec3.sum( + MyAvatar.position, + Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)) + ), + });*/ + } else if (isMesh) { + /*Entities.addEntity({ + type: "Model", + modelURL: `atp:${path}`, + shapeType: "simple-hull", + rotation: MyAvatar.orientation, + position: Vec3.sum( + MyAvatar.position, + Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)) + ), + });*/ + Entities.addModelEntity( + /* name */ path, + /* modelUrl */ `atp:${path}`, + /* textures */ "", + /* shapeType */ "simple-hull", + /* dynamic */ false, + /* collisionless */ false, + /* grabbable */ true, + /* position */ Vec3.sum( + MyAvatar.position, + Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)) + ), + /* gravity */ Vec3.ZERO + ); + } else { + console.error(`spawnEntity called with path that wasn't image or mesh! ${path}`); + } + } +} diff --git a/interface/resources/qml/overte/dialogs/BuiltinScriptsDialog.qml b/interface/resources/qml/overte/dialogs/BuiltinScriptsDialog.qml new file mode 100644 index 00000000000..0a19fa61147 --- /dev/null +++ b/interface/resources/qml/overte/dialogs/BuiltinScriptsDialog.qml @@ -0,0 +1,195 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.qmlmodels + +import ".." as Overte + +Rectangle { + anchors.fill: parent + implicitWidth: 400 + implicitHeight: 500 + visible: false + color: Overte.Theme.paletteActive.base + id: picker + + signal accepted(file: url) + signal rejected + + onAccepted: visible = false + onRejected: visible = false + + function open() { + visible = true; + } + + function getDirectoryModel(parentIndex = undefined) { + const DISPLAY_ROLE = 0; + const PATH_ROLE = 256; + const sourceModel = ScriptDiscoveryService.scriptsModel; + const sourceModelRootLength = sourceModel.rowCount(parentIndex); + let tmp = []; + + for (let i = 0; i < sourceModelRootLength; i++) { + const index = sourceModel.index(i, 0, parentIndex); + + const name = sourceModel.data(index, DISPLAY_ROLE); + const path = sourceModel.data(index, PATH_ROLE); + const hasChildren = sourceModel.hasChildren(index); + + if (hasChildren) { + tmp.push({ + name: name, + path: path, + rows: getDirectoryModel(index) + }); + } else { + tmp.push({ + name: name, + path: path, + }); + } + } + + tmp.sort((a, b) => ((b.rows ? 1 : 0) - (a.rows ? 1 : 0)) || a.name.localeCompare(b.name)); + + return tmp; + } + + component TreeDelegate: Rectangle { + required property TreeView treeView + required property int depth + required property int row + required property bool current + required property bool expanded + required property bool hasChildren + + property string name: treeView.model.getRow(treeView.index(row, 0)).name + + implicitWidth: treeView.contentWidth + implicitHeight: Overte.Theme.fontPixelSize * 2 + + color: { + if (current) { + return Overte.Theme.paletteActive.highlight; + } else if (row % 2 === 0) { + return Overte.Theme.paletteActive.base; + } else { + return Overte.Theme.paletteActive.alternateBase; + } + } + + // IconImage and ColorImage are private in Qt 6.10, + // so hijack AbstractButton's icon + Overte.Button { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: Overte.Theme.fontPixelSize * depth + id: indicator + + visible: hasChildren + width: parent.height + height: width + + flat: true + horizontalPadding: 0 + verticalPadding: 0 + + icon.source: expanded ? "../icons/triangle_down.svg" : "../icons/triangle_right.svg" + icon.width: Math.min(24, width) + icon.height: Math.min(24, width) + icon.color: current ? Overte.Theme.paletteActive.highlightedText : Overte.Theme.paletteActive.buttonText + + onClicked: treeView.toggleExpanded(row) + } + + Overte.Label { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: indicator.right + anchors.leftMargin: 4 + verticalAlignment: Text.AlignVCenter + color: current ? Overte.Theme.paletteActive.highlightedText : Overte.Theme.paletteActive.buttonText + text: name + } + } + + ColumnLayout { + anchors.fill: parent + + Overte.Label { + Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + wrapMode: Text.Wrap + text: qsTr("Load built-in script") + } + + Overte.Ruler { + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + } + + TreeView { + Layout.fillWidth: true + Layout.fillHeight: true + id: treeView + clip: true + + // QT6TODO: remove this once mouse input is working properly again, + // this needs to be false until then or the expander arrows are entirely unusable + interactive: false + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AlwaysOn + } + + contentWidth: width - ScrollBar.vertical.width + flickableDirection: Flickable.VerticalFlick + selectionModel: ItemSelectionModel {} + + model: TreeModel { + id: treeModel + + TableModelColumn { display: "name" } + + rows: getDirectoryModel() + } + + delegate: TreeDelegate {} + } + + RowLayout { + Layout.margins: 8 + Layout.fillWidth: true + + Item { Layout.fillWidth: true } + + Overte.Button { + Layout.preferredWidth: 128 + text: qsTr("Cancel") + + onClicked: rejected() + } + + Overte.Button { + Layout.preferredWidth: 128 + + enabled: { + if (treeView.selectionModel.currentIndex === -1) { return false; } + + const data = treeModel.getRow(treeView.selectionModel.currentIndex); + return !data?.rows; + } + text: qsTr("Load") + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + onClicked: { + const data = treeModel.getRow(treeView.selectionModel.currentIndex); + accepted(data.path); + } + } + } + } +} diff --git a/interface/resources/qml/overte/dialogs/FileDialog.qml b/interface/resources/qml/overte/dialogs/FileDialog.qml new file mode 100644 index 00000000000..beca7d6d55a --- /dev/null +++ b/interface/resources/qml/overte/dialogs/FileDialog.qml @@ -0,0 +1,610 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs as QtDialogs +import Qt.labs.folderlistmodel +import Qt.labs.qmlmodels + +import ".." +import "." + +Rectangle { + enum FileMode { + OpenFile, + OpenFiles, + SaveFile, + OpenFolder + } + + id: fileDialog + color: Theme.paletteActive.base + visible: false + + signal accepted + signal rejected + + property url currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + property list nameFilters: ["*"] + property int fileMode: FileDialog.OpenFile + + property url selectedFile: "" + property list selectedFiles: [] + + // TODO, FIXME + property list history: [] + property int historyIndex: 0 + + property string searchExpression: ".*" + + onAccepted: { + visible = false; + } + + onRejected: { + visible = false; + } + + function open() { + visible = true; + } + + function close() { + visible = false; + } + + function urlToPathString(urlRaw) { + const url = new URL(urlRaw); + + if (url.protocol !== "file:") { + return url; + } + + return url.pathname; + } + + function directoryRowActivated(currentData) { + const parentDir = new URL(currentFolder).pathname.match(/^\/?(.*)/)[1]; + const path = `file:///${parentDir}${parentDir ? "/" : ""}${currentData.fileName}`; + + if (currentData.fileIsDir) { + currentFolder = path; + tableView.selectionModel.clear(); + } else { + selectedFile = path; + selectedFiles = []; + + let possibleConflict = false; + + for (const index of tableView.selectionModel.selectedRows(0)) { + const rowData = tableView.model.getRow(index.row); + selectedFiles.push(`${currentFolder.toString()}/${rowData.fileName}`); + + if (rowData.fileName === saveName.text) { + possibleConflict = true; + } + } + + if (fileMode === FileDialog.FileMode.SaveFile && possibleConflict) { + replaceWarningDialog.text = qsTr("Are you sure you want to replace %1?").arg(saveName.text); + replaceWarningDialog.open(); + } else { + fileDialog.accepted(); + } + } + } + + ColumnLayout { + anchors.fill: parent + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.topMargin: 8 + + RoundButton { + icon.source: "../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Theme.paletteActive.buttonText + + enabled: historyIndex > 0 + + // TODO + onClicked: {} + } + RoundButton { + icon.source: "../icons/triangle_right.svg" + icon.width: 24 + icon.height: 24 + icon.color: Theme.paletteActive.buttonText + + enabled: historyIndex < history.length - 1 + + // TODO + onClicked: {} + } + + RoundButton { + icon.source: "../icons/arrow_up.svg" + icon.width: 24 + icon.height: 24 + icon.color: Theme.paletteActive.buttonText + + enabled: folderModel.parentFolder.toString() !== "" + + onClicked: { + currentFolder = folderModel.parentFolder; + } + } + + TextField { + Layout.fillWidth: true + Layout.preferredHeight: parent.height + + text: urlToPathString(fileDialog.currentFolder) + visible: pathEditToggle.checked + + Keys.onEnterPressed: { + fileDialog.currentFolder = `file:///${text.match(/^\/?(.*)/)[1]}`; + } + Keys.onReturnPressed: { + fileDialog.currentFolder = `file:///${text.match(/^\/?(.*)/)[1]}`; + } + } + + ListView { + Layout.fillWidth: true + Layout.preferredHeight: parent.height + + id: breadcrumbBar + visible: !pathEditToggle.checked + clip: true + pixelAligned: true + boundsBehavior: Flickable.StopAtBounds + orientation: Qt.Horizontal + + onCountChanged: { + positionViewAtEnd(); + } + + model: { + let breadcrumbs = []; + + let parts = new URL(currentFolder).pathname.split("/").filter(Boolean); + + if (SystemInformation.kernelType !== "winnt") { + parts.unshift(""); + } + + for (let i = 0; i < parts.length; i++) { + let targetUrl = parts.slice(1, i + 1).join("/"); + if (targetUrl === "") { + targetUrl = "file:///"; + } else { + targetUrl = "file:///" + targetUrl; + } + + breadcrumbs.push({ + label: parts[i] === "" ? "/" : parts[i], + targetUrl: targetUrl, + }); + } + + return breadcrumbs; + } + + delegate: Button { + required property string label + required property url targetUrl + + height: breadcrumbBar.height + text: label + + onClicked: { + currentFolder = targetUrl; + } + } + } + + RoundButton { + checkable: true + icon.source: "../icons/pencil.svg" + icon.color: Theme.paletteActive.buttonText + icon.width: 24 + icon.height: 24 + + id: pathEditToggle + } + } + + RowLayout { + Layout.fillWidth: true + + TextField { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.preferredHeight: parent.height + + id: searchField + placeholderText: qsTr("Search…") + + Keys.onEnterPressed: searchButton.click() + Keys.onReturnPressed: searchButton.click() + } + + RoundButton { + Layout.rightMargin: 8 + + id: searchButton + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Theme.paletteActive.buttonText + + onClicked: { + searchExpression = searchField.text === "" ? ".*" : searchField.text; + + // force a refresh + folderModel.folder = ""; + folderModel.folder = fileDialog.currentFolder; + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + visible: folderModel.status === FolderListModel.Null + + ColumnLayout { + anchors.centerIn: parent + spacing: 8 + + Label { + Layout.fillWidth: true + + text: qsTr("Invalid or unreachable folder") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } + + Button { + Layout.alignment: Qt.AlignHCenter + + text: qsTr("Go Home") + onClicked: { + currentFolder = StandardPaths.writableLocation(StandardPaths.HomeLocation); + } + } + } + } + + TableView { + Layout.fillWidth: true + Layout.fillHeight: true + visible: folderModel.status !== FolderListModel.Null + + // QT6TODO: Double-clicking on stuff flat out doesn't work yet, + // remove this once clicks on the overlay desktop work again + interactive: false + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AlwaysOn + interactive: true + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + rightMargin: ScrollBar.vertical.width + + id: tableView + clip: true + selectionBehavior: TableView.SelectRows + selectionMode: ( + fileDialog.fileMode === FileDialog.FileMode.OpenFiles ? + TableView.ExtendedSelection : + TableView.SingleSelection + ) + pixelAligned: true + rowSpacing: 1 + columnSpacing: 0 + boundsBehavior: Flickable.StopAtBounds + + model: TableModel { + TableModelColumn { + display: "fileName" + textAlignment: () => Text.AlignLeft + } + TableModelColumn { + display: "fileModified" + textAlignment: () => Text.AlignRight + } + TableModelColumn { + display: "fileSize" + textAlignment: () => Text.AlignLeft + } + } + + FolderListModel { + id: folderModel + nameFilters: fileDialog.nameFilters + folder: fileDialog.currentFolder + showDirsFirst: true + showFiles: fileDialog.fileMode !== FileDialog.OpenFolder + showOnlyReadable: true + caseSensitive: false + sortCaseSensitive: false + + // TableModel can't have a ListModel for its rows, + // so we have to turn it into a JS array + onStatusChanged: { + if (folderModel.status === FolderListModel.Ready) { + if (folder === "") { return; } + + let data = []; + + for (let i = 0; i < folderModel.count; i++) { + let datum = { + fileName: folderModel.get(i, "fileName"), + fileModified: folderModel.get(i, "fileModified").toLocaleString(null, Locale.ShortFormat), + fileSize: folderModel.get(i, "fileSize"), + fileIsDir: folderModel.get(i, "fileIsDir"), + }; + + if (!datum.fileName.match(new RegExp(searchExpression, "i"))) { + continue; + } + + if (datum.fileSize > 1024 * 1024 * 1024) { + let value = datum.fileSize / (1024 * 1024 * 1024); + value = Math.round(value * 100) / 100; + datum.fileSize = `${value} GiB`; + } else if (datum.fileSize > 1024 * 1024) { + let value = datum.fileSize / (1024 * 1024); + value = Math.round(value * 100) / 100; + datum.fileSize = `${value} MiB`; + } else if (datum.fileSize > 1024) { + let value = datum.fileSize / 1024; + value = Math.round(value * 100) / 100; + datum.fileSize = `${value} KiB`; + } else { + datum.fileSize = qsTr("%n byte(s)", "", datum.fileSize); + } + + if (datum.fileIsDir) { + datum.fileSize = qsTr("Folder"); + } + + data.push(datum); + } + + tableView.model.rows = data; + } else { + tableView.model.rows = []; + } + + tableView.positionViewAtRow(0, TableView.AlignVCenter, Qt.point(0, 0), Qt.rect(0, 0, 0, 0)); + } + } + + selectionModel: ItemSelectionModel { + onCurrentChanged: (index, _) => { + if (index.row === -1) { return; } + + const data = tableView.model.getRow(index.row); + selectedFile = `${currentFolder.toString()}/${data.fileName}`; + } + } + + delegate: Rectangle { + required property bool selected + required property string display + required property int textAlignment + + color: ( + selected ? + Theme.paletteActive.highlight : + (row % 2 !== 0) ? Theme.paletteActive.alternateBase : Theme.paletteActive.base + ) + + id: cell + implicitHeight: { + // hide the mtime column if the window isn't big enough to fit it comfortably + if (column === 1 && tableView.width < 720) { + return 0; + } else { + return text.implicitHeight * 2 + } + } + implicitWidth: { + let nameWidth = tableView.width; + let mtimeWidth = tableView.width * (1 / 4); + let sizeWidth = tableView.width * (1 / 4); + + // qt doesn't let us do stretchy columns so emulate it ourselves + nameWidth -= sizeWidth; + nameWidth -= mtimeWidth; + + // hide the mtime column if the window isn't big enough to fit it comfortably + if (tableView.width < 720) { + // can't be zero or qt complains + nameWidth += mtimeWidth; + mtimeWidth = 1; + } + + // how come there's one extra pixel? + nameWidth -= tableView.rightMargin + 1; + + switch (column) { + case 0: return nameWidth; + case 1: return mtimeWidth; + case 2: return sizeWidth; + } + } + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + + onPressed: mouse => { + parent.forceActiveFocus(); + + const index = tableView.model.index(row, column); + + if ( + fileMode === FileDialog.FileMode.OpenFiles && + (mouse.modifiers & Qt.ControlModifier) === Qt.ControlModifier + ) { + tableView.selectionModel.select( + index, + ItemSelectionModel.Toggle | ItemSelectionModel.Rows + ); + } else if ( + fileMode === FileDialog.FileMode.OpenFiles && + (mouse.modifiers & Qt.ShiftModifier) === Qt.ShiftModifier + ) { + const prevRow = tableView.selectionModel.currentIndex.row; + const currentRow = row; + const start = prevRow < currentRow ? prevRow : currentRow; + const end = prevRow < currentRow ? currentRow : prevRow; + + // select everything between the previous "current" row and the next "current" row + for (let i = start; i <= end; i++) { + tableView.selectionModel.select( + tableView.model.index(i, 0), + ItemSelectionModel.Select | ItemSelectionModel.Rows + ); + } + } else { + tableView.selectionModel.select( + index, + ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows + ); + } + + tableView.selectionModel.setCurrentIndex(index, ItemSelectionModel.Current); + + if (fileMode === FileDialog.FileMode.SaveFile) { + const rowData = tableView.model.getRow(index.row); + saveName.text = !rowData.fileIsDir ? rowData.fileName : ""; + } + } + + onDoubleClicked: { + directoryRowActivated(tableView.model.getRow(row)); + } + } + + Text { + id: text + anchors.fill: parent + anchors.leftMargin: 6 + anchors.rightMargin: 6 + + visible: parent.implicitWidth > 1 + text: cell.display + color: ( + selected ? + Theme.paletteActive.highlightedText : + Theme.paletteActive.text + ) + font.family: Theme.fontFamily + font.pixelSize: Theme.fontPixelSize + horizontalAlignment: cell.textAlignment + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } + } + } + + RowLayout { + visible: fileMode === FileDialog.FileMode.SaveFile + + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + + TextField { + Layout.fillWidth: true + + id: saveName + placeholderText: qsTr("Saved file name") + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 8 + + Label { + Layout.fillWidth: true + Layout.rightMargin: 8 + Layout.preferredHeight: parent.height + + text: nameFilters.join(", ") + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + Button { + implicitWidth: 128 + text: qsTr("Cancel") + + Component.onCompleted: { + clicked.connect(fileDialog.rejected) + } + } + + Button { + implicitWidth: 128 + backgroundColor: Theme.paletteActive.buttonAdd + text: { + if (fileMode === FileDialog.FileMode.OpenFolder) { + return qsTr("Open Here"); + } + + let text = fileMode === FileDialog.FileMode.SaveFile ? qsTr("Save") : qsTr("Open"); + + const currentRow = tableView.selectionModel.currentIndex.row; + if (currentRow !== -1) { + const currentData = tableView.model.getRow(currentRow); + + if (currentData.fileIsDir) { + text = qsTr("Open"); + } + } + + return text; + } + + enabled: tableView.selectionModel.hasSelection || fileMode === FileDialog.FileMode.OpenFolder + onClicked: { + if (fileMode === FileDialog.FileMode.OpenFolder) { + selectedFile = currentFolder; + fileDialog.accepted(); + } else { + const currentRow = tableView.selectionModel.currentIndex.row; + const currentData = tableView.model.getRow(currentRow); + directoryRowActivated(currentData); + } + } + } + } + } + + MessageDialog { + id: replaceWarningDialog + anchors.fill: parent + buttons: QtDialogs.MessageDialog.Yes | QtDialogs.MessageDialog.No + + onAccepted: { + fileDialog.accepted(); + } + } +} diff --git a/interface/resources/qml/overte/dialogs/README.md b/interface/resources/qml/overte/dialogs/README.md new file mode 100644 index 00000000000..cffd0b0ea75 --- /dev/null +++ b/interface/resources/qml/overte/dialogs/README.md @@ -0,0 +1 @@ +These are top-level windowed dialogs. They aren't derived from Dialog, which is an inline modal popup. diff --git a/interface/resources/qml/overte/dialogs/RunningScriptsDialog.qml b/interface/resources/qml/overte/dialogs/RunningScriptsDialog.qml new file mode 100644 index 00000000000..674d0cbd4a4 --- /dev/null +++ b/interface/resources/qml/overte/dialogs/RunningScriptsDialog.qml @@ -0,0 +1,312 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import ".." as Overte +import "." as OverteDialogs + +Rectangle { + anchors.fill: parent + + id: root + implicitWidth: 480 + implicitHeight: 720 + color: Overte.Theme.paletteActive.base + + property list runningScriptsModel: [] + + function refreshRunningScriptsModel() { + let tmp = []; + + for (const script of ScriptDiscoveryService.getRunning()) { + tmp.push({ + name: script.name, + url: script.url, + }); + } + + tmp.sort((a, b) => a.name.localeCompare(b.name)); + + runningScriptsModel = tmp; + } + + Connections { + target: ScriptDiscoveryService + + function onScriptCountChanged() { + refreshRunningScriptsModel(); + } + } + + Component.onCompleted: refreshRunningScriptsModel() + + component ScriptDelegate: Rectangle { + required property int index + required property string name + required property string url + + width: runningList.width - Overte.Theme.scrollbarWidth + height: layout.implicitHeight + + color: ( + index % 2 == 0 ? + Overte.Theme.paletteActive.base : + Overte.Theme.paletteActive.alternateBase + ) + + RowLayout { + id: layout + anchors.fill: parent + + ColumnLayout { + Layout.margins: 8 + + Overte.Label { + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + Layout.fillWidth: true + elide: Text.ElideRight + text: name.replace(/(.*).js$/, "$1"); + } + + Overte.Label { + Layout.alignment: Qt.AlignLeft | Qt.AlignBottom + Layout.fillWidth: true + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + elide: Text.ElideRight + text: { + if (url.startsWith("qrc:")) { + return qsTr("Built-in: %1").arg(url.toString().replace(/qrc:\/+(.*)$/, "$1")); + } else if (url.startsWith("file:///~/")) { + return qsTr("Built-in: %1").arg(url.toString().replace(/file:\/+\~\/+(.*)$/, "$1")); + } else { + return url; + } + } + } + } + + Row { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.margins: 8 + spacing: 2 + + Overte.RoundButton { + backgroundColor: Overte.Theme.paletteActive.buttonInfo + + icon.source: "../icons/copy.svg" + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 20 + icon.height: 20 + + implicitWidth: 28 + implicitHeight: 28 + horizontalPadding: 0 + verticalPadding: 0 + + onClicked: WindowScriptingInterface.copyToClipboard(url) + + Overte.ToolTip { text: qsTr("Copy URL to clipboard") } + } + + Overte.RoundButton { + icon.source: "../icons/reload.svg" + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 20 + icon.height: 20 + + implicitWidth: 28 + implicitHeight: 28 + horizontalPadding: 0 + verticalPadding: 0 + + // restart the script + onClicked: ScriptDiscoveryService.stopScript(url, true) + + Overte.ToolTip { text: qsTr("Reload") } + } + + Overte.RoundButton { + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + + icon.source: "../icons/close.svg" + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 20 + icon.height: 20 + + implicitWidth: 28 + implicitHeight: 28 + horizontalPadding: 0 + verticalPadding: 0 + + onClicked: ScriptDiscoveryService.stopScript(url) + + Overte.ToolTip { text: qsTr("Stop and remove") } + } + } + } + } + + ColumnLayout { + anchors.fill: parent + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AlwaysOn + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + } + + id: runningList + clip: true + model: root.runningScriptsModel + delegate: ScriptDelegate {} + } + + Overte.Ruler { Layout.fillWidth: true } + + Overte.Label { + Layout.fillWidth: true + + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Add new script from...") + } + + RowLayout { + Layout.margins: 8 + Layout.fillHeight: true + + Overte.Button { + // force equal button widths + Layout.preferredWidth: 1 + Layout.fillWidth: true + + text: qsTr("File") + + onClicked: fileDialog.open() + } + + Overte.Button { + // force equal button widths + Layout.preferredWidth: 1 + Layout.fillWidth: true + + text: qsTr("URL") + + onClicked: addFromUrlDialog.open() + } + + Overte.Button { + // force equal button widths + Layout.preferredWidth: 1 + Layout.fillWidth: true + + text: qsTr("Built-in") + + onClicked: builtinDialog.open() + } + } + } + + Overte.Dialog { + id: addFromUrlDialog + anchors.fill: parent + maxWidth: -1 + + signal accepted + signal rejected + + onAccepted: { + ScriptDiscoveryService.loadScript(targetUrlField.text, !oneSessionSwitch.checked); + close(); + + targetUrlField.text = ""; + oneSessionSwitch.checked = false; + } + + onRejected: { + close(); + + targetUrlField.text = ""; + oneSessionSwitch.checked = false; + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: qsTr("Load script from URL") + } + Overte.Ruler { Layout.fillWidth: true } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: qsTr("Script URL") + id: targetUrlField + } + + Overte.Switch { + id: oneSessionSwitch + text: qsTr("Only for this session") + } + + RowLayout { + Layout.preferredWidth: 720 + Layout.fillWidth: true + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + text: qsTr("Cancel") + + onClicked: { + addFromUrlDialog.rejected(); + } + } + + Item { + Layout.preferredWidth: 1 + Layout.fillWidth: true + } + + Overte.Button { + Layout.fillWidth: true + Layout.preferredWidth: 1 + + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Load") + enabled: targetUrlField.text !== "" + + onClicked: { + addFromUrlDialog.accepted(); + } + } + } + } + } + + OverteDialogs.BuiltinScriptsDialog { + anchors.fill: parent + id: builtinDialog + + onAccepted: file => ScriptDiscoveryService.loadScript(file, true) + } + + OverteDialogs.FileDialog { + anchors.fill: parent + id: fileDialog + + nameFilters: ["*.js"] + + onAccepted: ScriptDiscoveryService.loadScript(fileDialog.selectedFile, true) + } +} diff --git a/interface/resources/qml/overte/dialogs/qmldir b/interface/resources/qml/overte/dialogs/qmldir new file mode 100644 index 00000000000..86450bd753a --- /dev/null +++ b/interface/resources/qml/overte/dialogs/qmldir @@ -0,0 +1,5 @@ +module Dialogs +FileDialog 1.0 FileDialog.qml +AssetDialog 1.0 AssetDialog.qml +RunningScriptsDialog 1.0 RunningScriptsDialog.qml +BuiltinScriptsDialog 1.0 BuiltinScriptsDialog.qml diff --git a/interface/resources/qml/overte/icons/add_friend.svg b/interface/resources/qml/overte/icons/add_friend.svg new file mode 100644 index 00000000000..3894d6a3b95 --- /dev/null +++ b/interface/resources/qml/overte/icons/add_friend.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/admin_shield.svg b/interface/resources/qml/overte/icons/admin_shield.svg new file mode 100644 index 00000000000..14dc66daab1 --- /dev/null +++ b/interface/resources/qml/overte/icons/admin_shield.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/arrow_up.svg b/interface/resources/qml/overte/icons/arrow_up.svg new file mode 100644 index 00000000000..b313e472a5c --- /dev/null +++ b/interface/resources/qml/overte/icons/arrow_up.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/avatars.png b/interface/resources/qml/overte/icons/avatars.png new file mode 100644 index 00000000000..95d11f284d0 Binary files /dev/null and b/interface/resources/qml/overte/icons/avatars.png differ diff --git a/interface/resources/qml/overte/icons/broadcast.svg b/interface/resources/qml/overte/icons/broadcast.svg new file mode 100644 index 00000000000..e3887ffe945 --- /dev/null +++ b/interface/resources/qml/overte/icons/broadcast.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/close.svg b/interface/resources/qml/overte/icons/close.svg new file mode 100644 index 00000000000..c640a18e28d --- /dev/null +++ b/interface/resources/qml/overte/icons/close.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/copy.svg b/interface/resources/qml/overte/icons/copy.svg new file mode 100644 index 00000000000..98cde8781af --- /dev/null +++ b/interface/resources/qml/overte/icons/copy.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/delete.svg b/interface/resources/qml/overte/icons/delete.svg new file mode 100644 index 00000000000..26fd93f4e17 --- /dev/null +++ b/interface/resources/qml/overte/icons/delete.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/eye_closed.svg b/interface/resources/qml/overte/icons/eye_closed.svg new file mode 100644 index 00000000000..35905a61f5b --- /dev/null +++ b/interface/resources/qml/overte/icons/eye_closed.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/eye_open.svg b/interface/resources/qml/overte/icons/eye_open.svg new file mode 100644 index 00000000000..983636e21f7 --- /dev/null +++ b/interface/resources/qml/overte/icons/eye_open.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/filter.svg b/interface/resources/qml/overte/icons/filter.svg new file mode 100644 index 00000000000..75e22db7db6 --- /dev/null +++ b/interface/resources/qml/overte/icons/filter.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/folder.svg b/interface/resources/qml/overte/icons/folder.svg new file mode 100644 index 00000000000..6e1d56a35ce --- /dev/null +++ b/interface/resources/qml/overte/icons/folder.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/gold_star.svg b/interface/resources/qml/overte/icons/gold_star.svg new file mode 100644 index 00000000000..22cbc130ee8 --- /dev/null +++ b/interface/resources/qml/overte/icons/gold_star.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/home.svg b/interface/resources/qml/overte/icons/home.svg new file mode 100644 index 00000000000..f0f8acf355b --- /dev/null +++ b/interface/resources/qml/overte/icons/home.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/info.svg b/interface/resources/qml/overte/icons/info.svg new file mode 100644 index 00000000000..057d846de31 --- /dev/null +++ b/interface/resources/qml/overte/icons/info.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/pause.svg b/interface/resources/qml/overte/icons/pause.svg new file mode 100644 index 00000000000..aed337a41ef --- /dev/null +++ b/interface/resources/qml/overte/icons/pause.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/pencil.svg b/interface/resources/qml/overte/icons/pencil.svg new file mode 100644 index 00000000000..2882fb5b046 --- /dev/null +++ b/interface/resources/qml/overte/icons/pencil.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/plus.svg b/interface/resources/qml/overte/icons/plus.svg new file mode 100644 index 00000000000..e910fffc74d --- /dev/null +++ b/interface/resources/qml/overte/icons/plus.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/reload.svg b/interface/resources/qml/overte/icons/reload.svg new file mode 100644 index 00000000000..3c641fa6b4c --- /dev/null +++ b/interface/resources/qml/overte/icons/reload.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/remove_friend.svg b/interface/resources/qml/overte/icons/remove_friend.svg new file mode 100644 index 00000000000..1858c67f977 --- /dev/null +++ b/interface/resources/qml/overte/icons/remove_friend.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/search.svg b/interface/resources/qml/overte/icons/search.svg new file mode 100644 index 00000000000..dc72dcea553 --- /dev/null +++ b/interface/resources/qml/overte/icons/search.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/send.svg b/interface/resources/qml/overte/icons/send.svg new file mode 100644 index 00000000000..9d81595d392 --- /dev/null +++ b/interface/resources/qml/overte/icons/send.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/settings_cog.svg b/interface/resources/qml/overte/icons/settings_cog.svg new file mode 100644 index 00000000000..1662ccd361b --- /dev/null +++ b/interface/resources/qml/overte/icons/settings_cog.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/skip_backward.svg b/interface/resources/qml/overte/icons/skip_backward.svg new file mode 100644 index 00000000000..d8075f911c1 --- /dev/null +++ b/interface/resources/qml/overte/icons/skip_backward.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/skip_forward.svg b/interface/resources/qml/overte/icons/skip_forward.svg new file mode 100644 index 00000000000..c4696316862 --- /dev/null +++ b/interface/resources/qml/overte/icons/skip_forward.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/speaker_active.svg b/interface/resources/qml/overte/icons/speaker_active.svg new file mode 100644 index 00000000000..3f9a72c1118 --- /dev/null +++ b/interface/resources/qml/overte/icons/speaker_active.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/speaker_inactive.svg b/interface/resources/qml/overte/icons/speaker_inactive.svg new file mode 100644 index 00000000000..2ba23eb8023 --- /dev/null +++ b/interface/resources/qml/overte/icons/speaker_inactive.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/speaker_muted.svg b/interface/resources/qml/overte/icons/speaker_muted.svg new file mode 100644 index 00000000000..88baaa884cf --- /dev/null +++ b/interface/resources/qml/overte/icons/speaker_muted.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/triangle_down.svg b/interface/resources/qml/overte/icons/triangle_down.svg new file mode 100644 index 00000000000..be961ea6755 --- /dev/null +++ b/interface/resources/qml/overte/icons/triangle_down.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/triangle_left.svg b/interface/resources/qml/overte/icons/triangle_left.svg new file mode 100644 index 00000000000..400b2a50bdf --- /dev/null +++ b/interface/resources/qml/overte/icons/triangle_left.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/triangle_right.svg b/interface/resources/qml/overte/icons/triangle_right.svg new file mode 100644 index 00000000000..0c09d409d9b --- /dev/null +++ b/interface/resources/qml/overte/icons/triangle_right.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/triangle_up.svg b/interface/resources/qml/overte/icons/triangle_up.svg new file mode 100644 index 00000000000..4f683634316 --- /dev/null +++ b/interface/resources/qml/overte/icons/triangle_up.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/unset_avatar.svg b/interface/resources/qml/overte/icons/unset_avatar.svg new file mode 100644 index 00000000000..033a6abaf3b --- /dev/null +++ b/interface/resources/qml/overte/icons/unset_avatar.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons/users.svg b/interface/resources/qml/overte/icons/users.svg new file mode 100644 index 00000000000..ba42470f859 --- /dev/null +++ b/interface/resources/qml/overte/icons/users.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons_src/add_friend.svg b/interface/resources/qml/overte/icons_src/add_friend.svg new file mode 100644 index 00000000000..3894d6a3b95 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/add_friend.svg @@ -0,0 +1,2 @@ + + diff --git a/interface/resources/qml/overte/icons_src/admin_shield.svg b/interface/resources/qml/overte/icons_src/admin_shield.svg new file mode 100644 index 00000000000..1414c45e590 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/admin_shield.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/arrow_up.svg b/interface/resources/qml/overte/icons_src/arrow_up.svg new file mode 100644 index 00000000000..86a760fb64a --- /dev/null +++ b/interface/resources/qml/overte/icons_src/arrow_up.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/avatars.svg b/interface/resources/qml/overte/icons_src/avatars.svg new file mode 100644 index 00000000000..a92c54814a7 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/avatars.svg @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/broadcast.svg b/interface/resources/qml/overte/icons_src/broadcast.svg new file mode 100644 index 00000000000..ea3f6b9f853 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/broadcast.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/close.svg b/interface/resources/qml/overte/icons_src/close.svg new file mode 100644 index 00000000000..a1fef407dc0 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/close.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/copy.svg b/interface/resources/qml/overte/icons_src/copy.svg new file mode 100644 index 00000000000..7c67bf09ee7 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/copy.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/delete.svg b/interface/resources/qml/overte/icons_src/delete.svg new file mode 100644 index 00000000000..1783ffb0565 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/delete.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/eye_closed.svg b/interface/resources/qml/overte/icons_src/eye_closed.svg new file mode 100644 index 00000000000..963d827da6a --- /dev/null +++ b/interface/resources/qml/overte/icons_src/eye_closed.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/eye_open.svg b/interface/resources/qml/overte/icons_src/eye_open.svg new file mode 100644 index 00000000000..d5988f287bc --- /dev/null +++ b/interface/resources/qml/overte/icons_src/eye_open.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/filter.svg b/interface/resources/qml/overte/icons_src/filter.svg new file mode 100644 index 00000000000..913041ad0d0 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/filter.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/folder.svg b/interface/resources/qml/overte/icons_src/folder.svg new file mode 100644 index 00000000000..f05c66fda90 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/folder.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/gold_star.svg b/interface/resources/qml/overte/icons_src/gold_star.svg new file mode 100644 index 00000000000..3cf28b19080 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/gold_star.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/home.svg b/interface/resources/qml/overte/icons_src/home.svg new file mode 100644 index 00000000000..bb60859c10f --- /dev/null +++ b/interface/resources/qml/overte/icons_src/home.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/info.svg b/interface/resources/qml/overte/icons_src/info.svg new file mode 100644 index 00000000000..fe836b5fb48 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/info.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/pause.svg b/interface/resources/qml/overte/icons_src/pause.svg new file mode 100644 index 00000000000..8e537ed0743 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/pause.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/pencil.svg b/interface/resources/qml/overte/icons_src/pencil.svg new file mode 100644 index 00000000000..fef919a80a6 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/pencil.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/plus.svg b/interface/resources/qml/overte/icons_src/plus.svg new file mode 100644 index 00000000000..74b09c41340 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/plus.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/reload.svg b/interface/resources/qml/overte/icons_src/reload.svg new file mode 100644 index 00000000000..8e315f2b86a --- /dev/null +++ b/interface/resources/qml/overte/icons_src/reload.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/remove_friend.svg b/interface/resources/qml/overte/icons_src/remove_friend.svg new file mode 100644 index 00000000000..d5906ccfb3d --- /dev/null +++ b/interface/resources/qml/overte/icons_src/remove_friend.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/search.svg b/interface/resources/qml/overte/icons_src/search.svg new file mode 100644 index 00000000000..b1a0037eb00 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/search.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/send.svg b/interface/resources/qml/overte/icons_src/send.svg new file mode 100644 index 00000000000..f6d42c4c67b --- /dev/null +++ b/interface/resources/qml/overte/icons_src/send.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/settings_cog.svg b/interface/resources/qml/overte/icons_src/settings_cog.svg new file mode 100644 index 00000000000..06f58ef8537 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/settings_cog.svg @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/skip_backward.svg b/interface/resources/qml/overte/icons_src/skip_backward.svg new file mode 100644 index 00000000000..9ff33f128fc --- /dev/null +++ b/interface/resources/qml/overte/icons_src/skip_backward.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/skip_forward.svg b/interface/resources/qml/overte/icons_src/skip_forward.svg new file mode 100644 index 00000000000..92c5ac673c3 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/skip_forward.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/speaker.svg b/interface/resources/qml/overte/icons_src/speaker.svg new file mode 100644 index 00000000000..c45eb64b1ad --- /dev/null +++ b/interface/resources/qml/overte/icons_src/speaker.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/triangle_down.svg b/interface/resources/qml/overte/icons_src/triangle_down.svg new file mode 100644 index 00000000000..61077b34108 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/triangle_down.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/triangle_left.svg b/interface/resources/qml/overte/icons_src/triangle_left.svg new file mode 100644 index 00000000000..d5c80b69e90 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/triangle_left.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/triangle_right.svg b/interface/resources/qml/overte/icons_src/triangle_right.svg new file mode 100644 index 00000000000..5e81b99d186 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/triangle_right.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/triangle_up.svg b/interface/resources/qml/overte/icons_src/triangle_up.svg new file mode 100644 index 00000000000..ec54a2ae794 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/triangle_up.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/unset_avatar.svg b/interface/resources/qml/overte/icons_src/unset_avatar.svg new file mode 100644 index 00000000000..32efcc20c69 --- /dev/null +++ b/interface/resources/qml/overte/icons_src/unset_avatar.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/icons_src/users.svg b/interface/resources/qml/overte/icons_src/users.svg new file mode 100644 index 00000000000..68c2b0e27bc --- /dev/null +++ b/interface/resources/qml/overte/icons_src/users.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/overte/more_apps/AppDelegate.qml b/interface/resources/qml/overte/more_apps/AppDelegate.qml new file mode 100644 index 00000000000..d2b1197d41d --- /dev/null +++ b/interface/resources/qml/overte/more_apps/AppDelegate.qml @@ -0,0 +1,229 @@ +import QtQuick +import QtQuick.Layouts + +import ".." as Overte + +Rectangle { + id: item + width: ListView.view.contentWidth + implicitHeight: column.implicitHeight + color: index % 2 === 0 ? Overte.Theme.paletteActive.base : Overte.Theme.paletteActive.alternateBase + + required property int index + + required property string name + required property string description + required property url icon + required property string author + required property string scriptUrl + required property string appID + required property string repoHost + + property bool running: moreApps.runningScripts.includes(scriptUrl) + property bool installed: moreApps.installedScripts.includes(scriptUrl) + property bool processing: false + + Connections { + target: moreApps + ignoreUnknownSignals: false + + function onRunningScriptsChanged() { + processing = false; + } + } + + ColumnLayout { + anchors.fill: parent + id: column + + RowLayout { + Layout.margins: 8 + Layout.fillWidth: true + + Overte.RoundButton { + id: dropdownButton + checkable: true + icon.source: checked ? "../icons/triangle_up.svg" : "../icons/triangle_down.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + } + + Rectangle { + Layout.preferredWidth: 64 + Layout.preferredHeight: 64 + color: Overte.Theme.paletteActive.appIconBackground + radius: Overte.Theme.borderRadius + + border.width: Math.max(2, Overte.Theme.borderWidth) + border.color: { + if (installed && running) { + return Overte.Theme.paletteActive.appInstalledRunning; + } else if (installed && !running) { + return Overte.Theme.paletteActive.appInstalledNotRunning; + } else if (!installed && running) { + return Overte.Theme.paletteActive.appNotInstalledRunning; + } else { + return Overte.Theme.paletteActive.appNotInstalled; + } + } + + Image { + anchors.fill: parent + anchors.margins: 8 + source: icon + sourceSize.width: width + sourceSize.height: height + } + } + + ColumnLayout { + Layout.fillWidth: true + + Overte.Label { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: name + } + + Overte.Label { + Layout.fillWidth: true + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + visible: !dropdownButton.checked + elide: Text.ElideRight + text: description + } + + Overte.Label { + Layout.fillWidth: true + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + visible: dropdownButton.checked + wrapMode: Text.Wrap + text: { + if (running && installed) { + return qsTr("Running"); + } else if (running && !installed) { + return qsTr("Running, not installed"); + } else if (!running && installed) { + return qsTr("Paused"); + } else { + return ""; + } + } + } + } + + Overte.RoundButton { + visible: installed || running + enabled: !processing + icon.source: "../icons/delete.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + + onClicked: { + processing = true; + ScriptDiscoveryService.stopScript(scriptUrl); + + // only variable assignments are automatically tracked by Qt + const indexOf = moreApps.installedScripts.indexOf(scriptUrl); + moreApps.installedScripts.splice(indexOf); + moreApps.installedScriptsChanged(); + + moreApps.refreshRunningScripts(); + moreApps.refreshFilteredModel(); + } + } + + Overte.RoundButton { + enabled: !processing + visible: installed && running + icon.source: "../icons/reload.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + onClicked: ScriptDiscoveryService.stopScript(scriptUrl, true) + } + + Overte.RoundButton { + enabled: !processing + visible: installed + icon.source: running ? "../icons/pause.svg" : "../icons/triangle_right.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + onClicked: { + if (running) { + ScriptDiscoveryService.stopScript(scriptUrl, false); + } else { + ScriptDiscoveryService.loadScript(scriptUrl, false); + } + + processing = true; + moreApps.refreshRunningScripts(); + moreApps.refreshFilteredModel(); + } + } + + Overte.RoundButton { + id: installButton + enabled: !processing + visible: !installed + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + icon.source: "../icons/plus.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + processing = true; + ScriptDiscoveryService.loadScript(scriptUrl, false); + + // only variable assignments are automatically tracked by Qt + moreApps.installedScripts.push(scriptUrl); + moreApps.installedScriptsChanged(); + + moreApps.refreshRunningScripts(); + moreApps.refreshFilteredModel(); + } + } + } + + RowLayout { + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.fillWidth: true + visible: dropdownButton.checked + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + text: qsTr("By %1").arg(author) + } + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.7 + text: `${appID} @ ${repoHost}` + } + } + + Overte.Label { + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.bottomMargin: 8 + Layout.fillWidth: true + visible: dropdownButton.checked + wrapMode: Text.Wrap + text: description + } + } +} diff --git a/interface/resources/qml/overte/more_apps/MoreApps.qml b/interface/resources/qml/overte/more_apps/MoreApps.qml new file mode 100644 index 00000000000..7adff94d1c9 --- /dev/null +++ b/interface/resources/qml/overte/more_apps/MoreApps.qml @@ -0,0 +1,468 @@ +import QtCore as QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import ".." as Overte +import "." + +Rectangle { + id: moreApps + implicitWidth: 480 + implicitHeight: 720 + color: Overte.Theme.paletteActive.base + + QtCore.Settings { + id: settings + category: "moreApp" + property list installedScripts: [] + property list repoSources: [] + } + + property alias installedScripts: settings.installedScripts + property alias repoSources: settings.repoSources + + property string searchExpression: ".*" + property list rawListModel: [] + property list filteredModel: [] + + property list runningScripts: [] + + // the running scripts list doesn't immediately update, so give it a bit to update + Timer { + id: runningScriptsRefreshTimer + running: false + repeat: false + interval: 500 + + onTriggered: { + runningScripts = ScriptDiscoveryService.getRunning().map(x => x.url); + } + } + + function refreshRunningScripts() { + runningScriptsRefreshTimer.start(); + } + + function refreshFilteredModel() { + const searchRegex = new RegExp(searchExpression, "i"); + let tmp = []; + + for (const item of rawListModel) { + // if onlyInstalled is checked and we're not running or installed, skip + if (onlyInstalled.checked) { + if ( + !runningScripts.includes(item.scriptUrl) && + !installedScripts.includes(item.scriptUrl) + ) { + continue; + } + } + + if ( + item.name.match(searchRegex) || + item.id.match(searchRegex) || + item.description.match(searchRegex) + ) { + tmp.push(item); + } + } + + tmp.sort((a, b) => ( + a.name.localeCompare(b.name) + )); + + filteredModel = tmp; + } + + onRawListModelChanged: refreshFilteredModel() + + function parseList(list, repo) { + if (list.version !== 2) { + console.warn(`App list "${list}" is version ${list.version}, expected 2`); + return; + } + const repoHost = (new URL(repo)).hostname; + + let tmp = []; + + for (const item of list.applicationList) { + if (!item.appActive) { continue; } + + const baseUrl = `${repo}/applications`; + const scriptUrl = `${baseUrl}/${item.appBaseDirectory}/${item.appScriptVersions.Stable}`; + + tmp.push({ + appID: item.appBaseDirectory, + name: item.appName, + description: item.appDescription, + scriptUrl: scriptUrl, + author: item.appAuthor, + icon: `${baseUrl}/${item.appBaseDirectory}/${item.appIcon}`, + repoHost: repoHost, + }); + } + + rawListModel = rawListModel.concat(tmp); + } + + property var pendingRequests: ({}) + + function fetchList(repo) { + let xhr = new XMLHttpRequest(); + + pendingRequests[repo] = { + abort: () => xhr.abort(), + expectedSize: 1, + currentSize: 0, + }; + + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { + const length = xhr.getResponseHeader("Content-Length"); + if (length !== "") { + pendingRequests[repo].expectedSize = Number(length); + } + } else if (xhr.readyState === XMLHttpRequest.LOADING) { + pendingRequests[repo].currentSize = xhr.response.length; + } else if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status !== 200) { + console.warn(repo, xhr.status, xhr.statusText); + downloadProgressLabel.updateText(); + delete pendingRequests[repo]; + pendingRequestsChanged(); + return; + } + + pendingRequests[repo].currentSize = xhr.response.length; + + try { + const data = JSON.parse(xhr.response); + parseList(data, repo); + } catch(e) { + console.error(repo, e); + } + + delete pendingRequests[repo]; + } + + downloadProgressLabel.updateText(); + pendingRequestsChanged(); + }; + + xhr.open("GET", `${repo}/applications/metadata.json`); + xhr.send(); + + pendingRequestsChanged(); + downloadProgressLabel.updateText(); + } + + function clearAllPendingRequests() { + for (const req of Object.values(pendingRequests)) { + req.abort(); + } + + pendingRequests = []; + } + + function fetchAllLists() { + // cancel any downloads that are already in-flight + // so we don't accidentally get two responses for the same repo + clearAllPendingRequests(); + + rawListModel = []; + filteredModel = []; + + for (const repo of repoSources) { + fetchList(repo); + } + } + + onRepoSourcesChanged: { + refreshRunningScripts(); + fetchAllLists(); + } + + Component.onCompleted: { + // don't put this in the default because it'll load + // the default first, *then* the actual repos stored + // in the settings. this way we don't double up on requests + if (repoSources.length === 0) { + repoSources.push("https://more.overte.org"); + } + } + + Component.onDestruction: { + // we're quitting, cancel any downloads that are already in-flight + // so they don't try to update items that have been destroyed + clearAllPendingRequests(); + } + + Component { + id: settingsPage + + ColumnLayout { + RowLayout { + Layout.fillWidth: true + + Overte.RoundButton { + icon.source: "../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + onClicked: stack.pop() + } + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Settings - More Apps") + } + } + + Item { + Layout.fillWidth: true + implicitHeight: 12 + } + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignBottom + text: qsTr("App Sources") + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + } + + Overte.Ruler { Layout.fillWidth: true } + + ListView { + Layout.fillWidth: true + + // QT6TODO: remove this when mouse inputs are working properly + interactive: false + + clip: true + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AlwaysOn + } + contentWidth: width - ScrollBar.vertical.width + + implicitHeight: (Overte.Theme.fontPixelSizeSmall * 3) * 6 + + model: moreApps.repoSources + delegate: Rectangle { + required property int index + readonly property var text: moreApps.repoSources[index] + + width: ListView.view.contentWidth + implicitHeight: Overte.Theme.fontPixelSizeSmall * 3 + color: index % 2 === 0 ? Overte.Theme.paletteActive.base : Overte.Theme.paletteActive.alternateBase + + Overte.Label { + anchors.margins: 4 + anchors.left: parent.left + anchors.right: sourceRemoveButton.right + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: Overte.Theme.fontPixelSizeSmall + elide: Text.ElideRight + + // FIXME: shouldn't ever be undefined, but sometimes is anyway? + text: parent.text ?? "undefined" + } + + Overte.RoundButton { + id: sourceRemoveButton + anchors.margins: 4 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + backgroundColor: ( + (hovered || Overte.Theme.highContrast) ? + Overte.Theme.paletteActive.buttonDestructive : + Overte.Theme.paletteActive.button + ) + + horizontalPadding: 0 + verticalPadding: 0 + implicitWidth: 32 + implicitHeight: 32 + + icon.source: "../icons/close.svg" + icon.width: 18 + icon.height: 18 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + // FIXME: this throws an error about not being able to + // resolve "moreApps", but it seems to work fine anyway? + moreApps.repoSources.splice(index, 1) + moreApps.repoSourcesChanged() + } + } + } + } + + RowLayout { + Layout.fillWidth: true + + Overte.TextField { + Layout.fillWidth: true + + id: newSourceField + placeholderText: qsTr("App list source URL") + } + + Overte.RoundButton { + icon.source: "../icons/plus.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + enabled: !!newSourceField.text.match(/^https?:\/\/.+/i) + + onClicked: { + moreApps.repoSources.push(newSourceField.text.replace(/\/$/g, "")); + moreApps.repoSourcesChanged(); + newSourceField.text = ""; + } + } + } + + // spacer + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + Overte.StackView { + id: stack + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: downloadProgressLabel.top + + initialItem: ColumnLayout { + RowLayout { + Layout.margins: 4 + + Overte.RoundButton { + icon.source: "../icons/settings_cog.svg" + icon.width: 24 + icon.height: 24 + + onClicked: stack.push(settingsPage.createObject(stack)) + } + + Overte.RoundButton { + // TODO: is the eye icon acceptable here? it feels too vague to grok + icon.source: checked ? "../icons/eye_closed.svg" : "../icons/eye_open.svg" + icon.width: 24 + icon.height: 24 + + id: onlyInstalled + checkable: true + onToggled: refreshFilteredModel() + + backgroundColor: ( + checked ? + Overte.Theme.paletteActive.highlight : + Overte.Theme.paletteActive.button + ) + color: ( + checked ? + Overte.Theme.paletteActive.highlightedText : + Overte.Theme.paletteActive.buttonText + ) + + Overte.ToolTip { text: qsTr("Only show installed and running apps") } + } + + Overte.TextField { + Layout.fillWidth: true + placeholderText: qsTr("Search…") + id: searchField + + Keys.onEnterPressed: { + searchButton.clicked(); + forceActiveFocus(); + } + + Keys.onReturnPressed: { + searchButton.clicked(); + forceActiveFocus(); + } + } + + Overte.RoundButton { + icon.source: "../icons/search.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + id: searchButton + + onClicked: { + moreApps.searchExpression = searchField.text; + moreApps.refreshFilteredModel(); + } + } + } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Loading %n app source(s)…", "", repoSources.length) + visible: rawListModel.length === 0 + } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + visible: rawListModel.length > 0 + clip: true + + // QT6TODO: broken mouse input, remove when that's fixed + interactive: false + + ScrollBar.vertical: Overte.ScrollBar {} + contentWidth: width - ScrollBar.vertical.width + + model: filteredModel + delegate: AppDelegate {} + } + } + } + + Overte.Label { + anchors.margins: 4 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + id: downloadProgressLabel + visible: Object.values(pendingRequests).length !== 0 + + function updateText() { + const requests = Object.values(pendingRequests); + + if (requests.length === 0) { + this.text = ""; + return; + } + + let accum = 0; + + for (const req of requests) { + accum = (req.currentSize / req.expectedSize); + } + + const percent = Math.round((accum / requests.length) * 100); + this.text = `${qsTr("Downloading…")} ${percent}%`; + } + } +} diff --git a/interface/resources/qml/overte/more_apps/qmldir b/interface/resources/qml/overte/more_apps/qmldir new file mode 100644 index 00000000000..31d350227ae --- /dev/null +++ b/interface/resources/qml/overte/more_apps/qmldir @@ -0,0 +1,3 @@ +module MoreApps +MoreApps 1.0 MoreApps.qml +AppDelegate 1.0 AppDelegate.qml diff --git a/interface/resources/qml/overte/place_picker/PlaceItem.qml b/interface/resources/qml/overte/place_picker/PlaceItem.qml new file mode 100644 index 00000000000..bdfcc20f433 --- /dev/null +++ b/interface/resources/qml/overte/place_picker/PlaceItem.qml @@ -0,0 +1,184 @@ +import QtQuick +import QtQuick.Layouts + +import ".." as Overte + +Rectangle { + id: item + color: compatible ? Overte.Theme.paletteActive.alternateBase : Overte.Theme.paletteActive.buttonDestructive + + implicitWidth: gridView.cellWidth + implicitHeight: gridView.cellHeight + + required property int index + readonly property var modelData: gridView.model[index] + + required property string name + readonly property string domainName: modelData.domain.name + readonly property url thumbnail: modelData.thumbnail ?? "" + readonly property bool compatible: modelData.compatibleProtocol ?? true + readonly property url placeUrl: `hifi://${name}${modelData.path}` + + readonly property int currentUsers: modelData.current_attendance ?? 0 + readonly property int maxUsers: { + const capacity = modelData.domain.capacity; + return (capacity !== 0) ? capacity : 9999; + } + + readonly property color textBackgroundColor: { + if (Overte.Theme.highContrast) { + return Overte.Theme.darkMode ? "black" : "white" + } else { + return Overte.Theme.darkMode ? "#d0000000" : "#e0ffffff" + } + } + + readonly property bool hovered: mouseArea.containsMouse || infoButton.hovered || joinButton.hovered + + Overte.Label { + anchors.margins: Overte.Theme.borderWidth + anchors.fill: item + visible: thumbnailImage.status !== Image.Ready + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: thumbnailImage.status === Image.Loading ? "Loading…" : "No icon" + } + + Image { + anchors.margins: Overte.Theme.borderWidth + anchors.fill: item + id: thumbnailImage + + source: thumbnail + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectCrop + } + + Rectangle { + anchors.margins: Overte.Theme.borderWidth + anchors.top: item.top + anchors.left: item.left + + width: Math.min(titleText.implicitWidth + 8, item.width - (2 * item.border.width)) + height: Math.min(titleText.implicitHeight + 8, item.height - (2 * item.border.width)) + color: textBackgroundColor + + Overte.Label { + anchors.margins: 4 + anchors.fill: parent + + id: titleText + text: name + font.bold: true + font.pixelSize: Overte.Theme.fontPixelSizeSmall + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignTop + } + } + + Rectangle { + anchors.margins: Overte.Theme.borderWidth + anchors.bottom: item.bottom + anchors.left: item.left + + width: Math.min(userCountText.implicitWidth + 8, item.width - (2 * item.border.width)) + height: Math.min(userCountText.implicitHeight + 8, item.height - (2 * item.border.width)) + color: textBackgroundColor + + Overte.Label { + anchors.margins: 4 + anchors.fill: parent + + id: userCountText + text: { + if (maxUsers === 9999) { + return `${domainName}: ${currentUsers}`; + } else { + return `${domainName}: ${currentUsers}/${maxUsers}`; + } + } + color: { + if (currentUsers === 0) { + return Overte.Theme.paletteActive.userCountEmpty; + } else if (currentUsers < maxUsers) { + return Overte.Theme.paletteActive.userCountActive; + } else { + return Overte.Theme.paletteActive.userCountFull; + } + } + font.bold: true + font.pixelSize: Overte.Theme.fontPixelSizeXSmall + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignTop + elide: Text.ElideLeft + } + } + + MouseArea { + anchors.fill: item + hoverEnabled: true + propagateComposedEvents: true + id: mouseArea + } + + Overte.RoundButton { + anchors.margins: 4 + anchors.top: item.top + anchors.right: item.right + id: infoButton + + implicitWidth: 32 + implicitHeight: 32 + horizontalPadding: 0 + verticalPadding: 0 + + opacity: { + if (item.hovered) { + return Overte.Theme.highContrast || hovered ? 1.0 : 0.9; + } else { + return 0.0; + } + } + backgroundColor: Overte.Theme.paletteActive.buttonInfo + + icon.source: "../icons/info.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: infoDialog.open(index) + } + + Overte.RoundButton { + anchors.margins: 4 + anchors.bottom: item.bottom + anchors.right: item.right + id: joinButton + + implicitWidth: 40 + implicitHeight: 40 + horizontalPadding: 0 + verticalPadding: 0 + + enabled: item.compatible + visible: item.compatible + opacity: { + if (item.hovered) { + return Overte.Theme.highContrast || hovered ? 1.0 : 0.9; + } else { + return 0.0; + } + } + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + icon.source: "../icons/triangle_right.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: placePicker.goToLocation(placeUrl) + } +} diff --git a/interface/resources/qml/overte/place_picker/PlacePicker.qml b/interface/resources/qml/overte/place_picker/PlacePicker.qml new file mode 100644 index 00000000000..0cef61fe0fa --- /dev/null +++ b/interface/resources/qml/overte/place_picker/PlacePicker.qml @@ -0,0 +1,675 @@ +import QtCore as QtCore +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Dialogs as QtDialogs + +import "../" as Overte +import "." + +Rectangle { + id: placePicker + anchors.fill: parent + color: Overte.Theme.paletteActive.base + implicitWidth: 480 + implicitHeight: 720 + + property bool hasHomeButton: true + + readonly property string protocolSignature: WindowScriptingInterface.protocolSignature() + + QtCore.Settings { + id: filters + category: "placesApp" + property string searchExpression: ".*" + property bool includeIncompatible: false + property list maturity: [ + "everyone", + "teen", + "mature", + "adult", + "unrated", + ] + property list favoritedPlaceIds: [] + } + + function goBack() { + let cookie = Date.now() + Math.floor(Math.random() * (1000 - -1000) + -1000); + + sendToScript(JSON.stringify({ + action: "system:location_go_back", + data: { cookie: cookie }, + })); + } + + function goForward() { + let cookie = Date.now() + Math.floor(Math.random() * (1000 - -1000) + -1000); + + sendToScript(JSON.stringify({ + action: "system:location_go_forward", + data: { cookie: cookie }, + })); + } + + function goToLocation(path) { + let cookie = Date.now() + Math.floor(Math.random() * (1000 - -1000) + -1000); + + sendToScript(JSON.stringify({ + action: "system:location_go_to", + data: { + cookie: cookie, + path: path, + }, + })); + } + + // The mv.overte.org directory listing is about 300KiB + // and takes about 10 seconds to download. Is there something + // we could use to safely cache the last results and only refresh + // after a set period? + property list rawPlaces: [] + property real downloadProgress: 0.0 + + function delayedShuffleHash(x) { + const ONE_DAY_MS = 24 * 3600 * 1000; + const SHUFFLE_CYCLE_MS = 5 * ONE_DAY_MS; + + const FNV_OFFSET = 0x811c9dc5; + const FNV_PRIME = 0x01000193; + const ALL_32BIT = 0xffffffff; + + let hashAccum = (FNV_OFFSET + Math.floor(Date.now() / SHUFFLE_CYCLE_MS)) & ALL_32BIT; + for (let p of x) { + // not quite FNV-1a, because we're counting 32-bit codepoints rather than bytes + hashAccum ^= p; + hashAccum = (hashAccum * FNV_PRIME) & ALL_32BIT; + } + + return hashAccum; + } + + function filterPlaces() { + // TODO: federation support + const hostname = (new URL(AccountServices.metaverseServerURL)).hostname; + const searchExpression = new RegExp(filters.searchExpression, "i"); + const ONE_DAY_SECS = 60 * 60 * 24; + let tmp = []; + + for (let place of rawPlaces) { + const compatibleProtocol = place.domain.protocol_version === protocolSignature; + // ?status=active should filter out dead places already + //const recentHeartbeat = ((Date.now() - parseInt(place.domain.time_of_last_heartbeat_s)) / 1000) < ONE_DAY_SECS; + const filterName = Boolean(place.name.match(searchExpression)); + const filterDomain = Boolean(place.domain.name.match(searchExpression)); + const filterDesc = Boolean(place.description.match(searchExpression)); + const filterMaturity = filters.maturity.includes(place.maturity); + const filterHasUsers = !onlyShowActiveButton.checked || place.current_attendance > 0; + + if ( + (compatibleProtocol || filters.includeIncompatible) && + (filterName || filterDomain || filterDesc) && + filterMaturity && + filterHasUsers + ) { + place.directoryHost = hostname; + place.compatibleProtocol = compatibleProtocol; + tmp.push(place); + } + } + + tmp.sort((a, b) => ( + // if "show incompatible servers" is on, sort compatible ones first + ((b.compatibleProtocol ? 1 : 0) - (a.compatibleProtocol ? 1 : 0)) || + // sort favorited places up + ((filters.favoritedPlaceIds.includes(b.placeId) ? 1 : 0) - (filters.favoritedPlaceIds.includes(a.placeId) ? 1 : 0)) || + // prefer places with a thumbnail + ((b.thumbnail ? 1 : 0) - (a.thumbnail ? 1 : 0)) || + // random shuffle that's stable for a few days to mix things up + delayedShuffleHash(b.placeId) - delayedShuffleHash(a.placeId) + )); + + gridView.model = tmp; + } + + Component.onCompleted: { + let fileSize = 1; + let xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { + const length = xhr.getResponseHeader("Content-Length"); + if (length !== "") { fileSize = Number(length); } + } else if (xhr.readyState === XMLHttpRequest.LOADING) { + downloadProgress = xhr.response.length / fileSize; + } else if (xhr.readyState === XMLHttpRequest.DONE) { + console.debug("Finished downloading place list"); + downloadProgress = 1.0; + + try { + const body = JSON.parse(xhr.responseText); + rawPlaces = body.data.places; + filterPlaces(); + } catch (e) { + console.error(e); + } + } + }; + + console.debug("Downloading place list…"); + downloadProgress = 0.0; + + // TODO: federation support, multiple directory sources + xhr.open("GET", `${AccountServices.metaverseServerURL}/api/v1/places?status=online`); + xhr.send(); + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + RowLayout { + Layout.margins: 4 + + Overte.RoundButton { + visible: placePicker.hasHomeButton + + icon.source: "../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: placePicker.goBack() + } + + Overte.RoundButton { + visible: placePicker.hasHomeButton + + icon.source: "../icons/triangle_right.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: placePicker.goForward() + } + + Overte.RoundButton { + visible: placePicker.hasHomeButton + + icon.source: "../icons/home.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: placePicker.goToLocation(LocationBookmarks.getHomeLocationAddress()) + + Overte.ToolTip { text: qsTr("Go to Home bookmark") } + } + + Overte.TextField { + Layout.fillWidth: true + + id: searchField + placeholderText: qsTr("Search…") + + Keys.onEnterPressed: { + searchButton.click(); + forceActiveFocus(); + } + Keys.onReturnPressed: { + searchButton.click(); + forceActiveFocus(); + } + } + + Overte.RoundButton { + id: searchButton + icon.source: ( + searchField.text.match(/(?:hifi|https?|file):\/\//) ? + "../icons/send.svg" : + "../icons/search.svg" + ) + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + if (searchField.text.startsWith("hifi://")) { + placePicker.goToLocation(infoDialog.placeUrl); + searchField.text = ""; + } else { + filters.searchExpression = searchField.text === "" ? ".*" : searchField.text; + filterPlaces(); + } + } + } + + Overte.RoundButton { + id: onlyShowActiveButton + icon.source: "../icons/users.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + checkable: true + + Overte.ToolTip { text: qsTr("Only show places with users") } + + onClicked: filterPlaces() + } + + Overte.RoundButton { + id: filterButton + icon.source: "../icons/filter.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: filterDialog.open() + + Overte.ToolTip { text: qsTr("Search options") } + } + } + + Overte.TabBar { + Layout.fillWidth: true + id: tabBar + + Overte.TabButton { text: qsTr("Public") } + Overte.TabButton { text: qsTr("Bookmarks") } + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + visible: rawPlaces.length === 0 + + Item { Layout.fillHeight: true } + + Overte.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: qsTr("Loading…") + } + + Rectangle { + Layout.margins: 8 + Layout.leftMargin: 64 + Layout.rightMargin: 64 + Layout.fillWidth: true + + implicitWidth: 256 + implicitHeight: Overte.Theme.fontPixelSize + (Overte.Theme.borderWidth * 2) + color: Overte.Theme.paletteActive.base + border.color: Qt.darker(color, Overte.Theme.borderDarker) + border.width: Overte.Theme.borderWidth + radius: Overte.Theme.borderRadius + + Rectangle { + x: parent.border.width + y: parent.border.width + height: parent.height - (parent.border.width * 2) + width: downloadProgress * (parent.width - (parent.border.width * 2)) + radius: parent.radius - Overte.Theme.borderWidth + color: Overte.Theme.paletteActive.highlight + } + } + + Item { Layout.fillHeight: true } + } + + StackLayout { + Layout.fillWidth: true + Layout.fillHeight: true + + GridView { + // QT6TODO: remove this once mouse inputs work properly + interactive: false + + id: gridView + visible: rawPlaces.length !== 0 + // fit two cells onto the default tablet + cellWidth: (480 - Overte.Theme.scrollbarWidth) / 2 + cellHeight: Math.floor(cellWidth * 0.6) + clip: true + + ScrollBar.vertical: Overte.ScrollBar {} + rightMargin: ScrollBar.vertical.width + + model: [] + delegate: PlaceItem {} + } + } + + RowLayout { + Layout.margins: 2 + Layout.fillWidth: true + + Overte.Label { + Layout.fillWidth: true + verticalAlignment: Text.AlignVCenter + text: ( + gridView.model.length !== 0 ? + qsTr("%1 place(s)").arg(gridView.model.length) : + "" + ) + } + + Overte.RoundButton { + id: settingsButton + icon.source: "../icons/settings_cog.svg" + icon.width: 24 + icon.height: 24 + + onClicked: console.error("TODO: settings") + visible: false + } + } + } + + Overte.Dialog { + id: filterDialog + visible: false + anchors.fill: placePicker + + function open() { + filterControlMaturityEveryone.checked = filters.maturity.includes("everyone"); + filterControlMaturityTeen.checked = filters.maturity.includes("teen"); + filterControlMaturityMature.checked = filters.maturity.includes("mature"); + filterControlMaturityAdult.checked = filters.maturity.includes("adult"); + filterControlIncludeIncompatible.checked = filters.includeIncompatible; + + visible = true; + opacity = Overte.Theme.reducedMotion ? 1 : 0; + } + + function accept() { + let maturityList = []; + + if (filterControlMaturityEveryone.checked) { + maturityList.push("everyone"); + } + + if (filterControlMaturityTeen.checked) { + maturityList.push("teen"); + } + + if (filterControlMaturityMature.checked) { + maturityList.push("mature"); + } + + if (filterControlMaturityAdult.checked) { + maturityList.push("adult"); + } + + // if everything is enabled or disabled, include unrated places too + if (maturityList.length === 0 || maturityList.length === 4) { + maturityList.push("unrated"); + } + + filters.includeIncompatible = filterControlIncludeIncompatible.checked; + filters.maturity = maturityList; + placePicker.filterPlaces(); + close(); + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + text: qsTr("Filters") + horizontalAlignment: Text.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + } + + Overte.Ruler { Layout.fillWidth: true } + + RowLayout { + Overte.Label { + Layout.fillWidth: true + text: qsTr("Maturity ratings") + } + + GridLayout { + Layout.alignment: Qt.AlignRight + columns: 2 + + Overte.Switch { + id: filterControlMaturityEveryone + text: qsTr("Everyone") + } + + Overte.Switch { + id: filterControlMaturityTeen + text: qsTr("Teen") + } + + Overte.Switch { + id: filterControlMaturityMature + text: qsTr("Mature") + } + + Overte.Switch { + id: filterControlMaturityAdult + text: qsTr("Adult") + } + } + } + + RowLayout { + Layout.fillWidth: true + + Overte.Label { + Layout.fillWidth: true + text: qsTr("Show incompatible servers") + } + + Overte.Switch { id: filterControlIncludeIncompatible } + } + + RowLayout { + Layout.preferredWidth: 480 + + Overte.Button { + Layout.preferredWidth: 1 + Layout.fillWidth: true + text: qsTr("Cancel") + onClicked: filterDialog.close() + } + + Item { Layout.fillWidth: true } + + Overte.Button { + Layout.preferredWidth: 1 + Layout.fillWidth: true + backgroundColor: Overte.Theme.paletteActive.buttonAdd + text: qsTr("Apply") + onClicked: filterDialog.accept() + } + } + } + } + + Overte.Dialog { + id: infoDialog + visible: false + anchors.fill: placePicker + + property string placeName: "" + property string placeDesc: "" + property string domainName: "" + property string directoryHost: "" + property int currentUsers: 0 + property int maxUsers: 0 + property list managers: [] + property url placeUrl: "" + property bool compatible: true + property string placeId: "" + + function open(index) { + const data = gridView.model[index]; + placeName = data.name; + domainName = data.domain.name; + placeDesc = data.description; + directoryHost = data.directoryHost; + compatible = data.compatibleProtocol; + + // ignore the redundant default place description + if ( + placeDesc === `A place in ${domainName}` || + placeDesc === `A place in ${placeName}` + ) { + placeDesc = ""; + } + + currentUsers = data.current_attendance; + maxUsers = data.domain.capacity; + managers = data.managers; + placeUrl = data.finalPlaceUrl ?? `hifi://${placeName}${data.path}`; + placeId = data.placeId; + + visible = true; + opacity = Overte.Theme.reducedMotion ? 1 : 0; + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + + ColumnLayout { + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Layout.fillWidth: true + + Overte.Label { + // leave space for the close button + Layout.rightMargin: 44 + Layout.fillWidth: true + font.bold: true + text: infoDialog.placeName + } + + Overte.Label { + // leave space for the close button + Layout.rightMargin: 44 + font.pixelSize: Overte.Theme.fontPixelSizeSmall + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + text: `${infoDialog.domainName} @ ${infoDialog.directoryHost}` + } + + Overte.Label { + Layout.fillWidth: true + wrapMode: Text.Wrap + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: qsTr("Managed by %1").arg(infoDialog.managers.join(", ")) + } + + Overte.Ruler { Layout.fillWidth: true } + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + wrapMode: Text.Wrap + font.pixelSize: Overte.Theme.fontPixelSizeSmall + text: infoDialog.placeDesc + } + } + + RowLayout { + implicitWidth: 480 + Layout.preferredWidth: 480 + Layout.fillWidth: true + + Overte.Button { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 1 + text: qsTr("Copy URL") + onClicked: WindowScriptingInterface.copyToClipboard(infoDialog.placeUrl) + } + + Overte.Button { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 1 + backgroundColor: Overte.Theme.paletteActive.buttonInfo + enabled: infoDialog.compatible + text: qsTr("Portal") + onClicked: { + infoDialog.close(); + Messages.sendMessage("org.overte.PlacePortal.Create", JSON.stringify({ + place_name: infoDialog.placeName, + place_url: infoDialog.placeUrl, + })); + } + } + + Overte.Button { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 1 + backgroundColor: Overte.Theme.paletteActive.buttonAdd + enabled: infoDialog.compatible + text: infoDialog.compatible ? qsTr("Join") : qsTr("Incompatible") + onClicked: { + infoDialog.close(); + placePicker.goToLocation(infoDialog.placeUrl); + } + } + } + } + + Row { + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 8 + spacing: 4 + + Overte.RoundButton { + implicitWidth: 28 + implicitHeight: 28 + + icon.source: "../icons/gold_star.svg" + icon.width: 20 + icon.height: 20 + icon.color: ( + checked ? + Overte.Theme.paletteActive.buttonText : + Overte.Theme.paletteActive.placeholderText + ) + + backgroundColor: checked ? Overte.Theme.paletteActive.buttonFavorite : Overte.Theme.paletteActive.button + checkable: true + checked: filters.favoritedPlaceIds.includes(infoDialog.placeId) + + onToggled: { + if (!checked) { + const index = filters.favoritedPlaceIds.indexOf(infoDialog.placeId); + filters.favoritedPlaceIds.splice(index); + } else { + filters.favoritedPlaceIds.push(infoDialog.placeId); + } + + filterPlaces(); + } + + Overte.ToolTip { text: qsTr("Favorite\nSorts this place before unfavorited places.") } + } + + Overte.RoundButton { + implicitWidth: 28 + implicitHeight: 28 + + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + + icon.source: "../icons/close.svg" + icon.width: 18 + icon.height: 18 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: infoDialog.close() + } + } + } +} diff --git a/interface/resources/qml/overte/place_picker/qmldir b/interface/resources/qml/overte/place_picker/qmldir new file mode 100644 index 00000000000..5b37f768aef --- /dev/null +++ b/interface/resources/qml/overte/place_picker/qmldir @@ -0,0 +1,3 @@ +module PlacePicker +PlacePicker 1.0 PlacePicker.qml +PlaceItem 1.0 PlaceItem.qml diff --git a/interface/resources/qml/overte/qmldir b/interface/resources/qml/overte/qmldir new file mode 100644 index 00000000000..f5e84c0e293 --- /dev/null +++ b/interface/resources/qml/overte/qmldir @@ -0,0 +1,19 @@ +module Overte +singleton Theme 1.0 Theme.qml +Button 1.0 Button.qml +TextField 1.0 TextField.qml +Switch 1.0 Switch.qml +TabBar 1.0 TabBar.qml +TabButton 1.0 TabButton.qml +Label 1.0 Label.qml +AppButton 1.0 AppButton.qml +RoundButton 1.0 RoundButton.qml +ScrollBar 1.0 ScrollBar.qml +ToolTip 1.0 ToolTip.qml +NodeGraph 1.0 NodeGraph.qml +Node 1.0 Node.qml +FileDialog 1.0 FileDialog.qml +Slider 1.0 Slider.qml +SpinBox 1.0 SpinBox.qml +BodyText 1.0 BodyText.qml +StackView 1.0 StackView.qml diff --git a/interface/resources/qml/overte/settings/ComboSetting.qml b/interface/resources/qml/overte/settings/ComboSetting.qml new file mode 100644 index 00000000000..467aa35cda0 --- /dev/null +++ b/interface/resources/qml/overte/settings/ComboSetting.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../" as Overte + +RowLayout { + property alias text: labelItem.text + property alias model: comboItem.model + property alias textRole: comboItem.textRole + property alias valueRole: comboItem.valueRole + property alias currentIndex: comboItem.currentIndex + property alias enabled: comboItem.enabled + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 16 + + Overte.Label { + // equally sized items + Layout.preferredWidth: 1 + Layout.fillWidth: true + + id: labelItem + wrapMode: Text.Wrap + } + + Overte.ComboBox { + // equally sized items + Layout.preferredWidth: 1 + Layout.fillWidth: true + + id: comboItem + } +} diff --git a/interface/resources/qml/overte/settings/FolderSetting.qml b/interface/resources/qml/overte/settings/FolderSetting.qml new file mode 100644 index 00000000000..dbf7001c290 --- /dev/null +++ b/interface/resources/qml/overte/settings/FolderSetting.qml @@ -0,0 +1,49 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte +import "../dialogs" as OverteDialogs + +ColumnLayout { + property alias text: labelItem.text + property alias value: textFieldItem.text + property bool enabled: true + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 4 + + Overte.Label { + Layout.alignment: Qt.AlignBottom + id: labelItem + wrapMode: Text.Wrap + } + + RowLayout { + Layout.fillWidth: true + + Overte.TextField { + Layout.fillWidth: true + + id: textFieldItem + enabled: item.enabled + } + + Overte.RoundButton { + enabled: item.enabled + + icon.source: "../icons/folder.svg" + icon.width: 24 + icon.height: 24 + + onClicked: settingsRoot.openFolderPicker( + folder => { + value = folder; + }, + value + ); + } + } +} diff --git a/interface/resources/qml/overte/settings/Header.qml b/interface/resources/qml/overte/settings/Header.qml new file mode 100644 index 00000000000..e5e5e9901c7 --- /dev/null +++ b/interface/resources/qml/overte/settings/Header.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte + +ColumnLayout { + property alias text: labelItem.text + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 2 + height: Overte.Theme.fontPixelSize * 3 + + Overte.Label { + id: labelItem + Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter + opacity: Overte.Theme.highContrast ? 1.0 : 0.6 + wrapMode: Text.Wrap + } + + Overte.Ruler { + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + } +} diff --git a/interface/resources/qml/overte/settings/SettingNote.qml b/interface/resources/qml/overte/settings/SettingNote.qml new file mode 100644 index 00000000000..4844b6af065 --- /dev/null +++ b/interface/resources/qml/overte/settings/SettingNote.qml @@ -0,0 +1,13 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte + +Overte.Label { + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + wrapMode: Text.Wrap + font.pixelSize: Overte.Theme.fontPixelSizeSmall +} diff --git a/interface/resources/qml/overte/settings/Settings.qml b/interface/resources/qml/overte/settings/Settings.qml new file mode 100644 index 00000000000..b69937cf422 --- /dev/null +++ b/interface/resources/qml/overte/settings/Settings.qml @@ -0,0 +1,71 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import "../" as Overte +import "." as OverteSettings +import "./pages" as SettingsPages +import "../dialogs" as OverteDialogs + +Rectangle { + id: settingsRoot + width: 480 + height: 720 + visible: true + anchors.fill: parent + color: Overte.Theme.paletteActive.base + + Overte.TabBar { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + id: tabBar + + Overte.TabButton { text: qsTr("General") } + Overte.TabButton { text: qsTr("Graphics") } + Overte.TabButton { text: qsTr("Controls") } + Overte.TabButton { text: qsTr("Audio") } + } + + StackLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + anchors.topMargin: Overte.Theme.fontPixelSize + currentIndex: tabBar.currentIndex + + SettingsPages.General {} + + SettingsPages.Graphics {} + + SettingsPages.Controls {} + + SettingsPages.Audio {} + } + + OverteDialogs.FileDialog { + anchors.fill: settingsRoot + visible: false + + id: folderDialog + + property var acceptedCallback: folder => {} + fileMode: OverteDialogs.FileDialog.OpenFolder + + onAccepted: { + acceptedCallback(new URL(folderDialog.selectedFile).pathname); + close(); + } + + onRejected: close() + } + + function openFolderPicker(callback, root) { + folderDialog.acceptedCallback = callback; + if (root) { + folderDialog.currentFolder = `file://${root}`; + } + folderDialog.open(); + } +} diff --git a/interface/resources/qml/overte/settings/SettingsPage.qml b/interface/resources/qml/overte/settings/SettingsPage.qml new file mode 100644 index 00000000000..9ce3826428c --- /dev/null +++ b/interface/resources/qml/overte/settings/SettingsPage.qml @@ -0,0 +1,23 @@ +import QtQuick +import QtQuick.Controls + +import "../" as Overte + +ScrollView { + default property alias children: column.children + + ScrollBar.vertical: Overte.ScrollBar { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + policy: ScrollBar.AsNeeded + } + contentWidth: width - ScrollBar.vertical.width + + Column { + id: column + anchors.fill: parent + spacing: 8 + padding: 16 + } +} diff --git a/interface/resources/qml/overte/settings/SliderSetting.qml b/interface/resources/qml/overte/settings/SliderSetting.qml new file mode 100644 index 00000000000..da70e463cfe --- /dev/null +++ b/interface/resources/qml/overte/settings/SliderSetting.qml @@ -0,0 +1,76 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../" as Overte + +ColumnLayout { + property alias text: labelItem.text + property alias value: sliderItem.value + property alias from: sliderItem.from + property alias to: sliderItem.to + property alias stepSize: sliderItem.stepSize + property alias enabled: sliderItem.enabled + property string valueText: valueToText() + property bool fineTweakButtons: false + + property var valueToText: () => value.toString() + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 16 + + RowLayout { + Overte.Label { + Layout.fillWidth: true + + id: labelItem + wrapMode: Text.Wrap + } + + Overte.Label { + text: valueText + wrapMode: Text.Wrap + } + } + + RowLayout { + Overte.RoundButton { + visible: fineTweakButtons + + icon.width: 24 + icon.height: 24 + icon.source: "../icons/triangle_left.svg" + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + sliderItem.value -= item.stepSize; + } + } + + Overte.Slider { + Layout.fillWidth: true + + id: sliderItem + snapMode: Slider.SnapAlways + } + + Overte.RoundButton { + visible: fineTweakButtons + + icon.width: 24 + icon.height: 24 + icon.source: "../icons/triangle_right.svg" + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + sliderItem.value += item.stepSize; + } + } + } + + // an extra spacer so the slider doesn't crowd with the setting below + Item {} +} diff --git a/interface/resources/qml/overte/settings/SpinBoxSetting.qml b/interface/resources/qml/overte/settings/SpinBoxSetting.qml new file mode 100644 index 00000000000..ca5e4d440e1 --- /dev/null +++ b/interface/resources/qml/overte/settings/SpinBoxSetting.qml @@ -0,0 +1,35 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../" as Overte + +RowLayout { + property alias text: labelItem.text + property alias value: spinboxItem.value + property alias from: spinboxItem.from + property alias to: spinboxItem.to + property alias enabled: spinboxItem.enabled + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 16 + + Overte.Label { + // equally sized items + Layout.preferredWidth: 1 + Layout.fillWidth: true + + id: labelItem + wrapMode: Text.Wrap + } + + Overte.SpinBox { + Layout.alignment: Qt.AlignRight + + id: spinboxItem + editable: true + } +} diff --git a/interface/resources/qml/overte/settings/SwitchSetting.qml b/interface/resources/qml/overte/settings/SwitchSetting.qml new file mode 100644 index 00000000000..3d928cf80fa --- /dev/null +++ b/interface/resources/qml/overte/settings/SwitchSetting.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte + +RowLayout { + property alias text: labelItem.text + property alias value: switchItem.checked + property bool enabled: true + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 16 + + Overte.Label { + Layout.fillWidth: true + + id: labelItem + wrapMode: Text.Wrap + } + + Overte.Switch { + id: switchItem + enabled: item.enabled + } +} diff --git a/interface/resources/qml/overte/settings/WideComboSetting.qml b/interface/resources/qml/overte/settings/WideComboSetting.qml new file mode 100644 index 00000000000..15490ba2c6d --- /dev/null +++ b/interface/resources/qml/overte/settings/WideComboSetting.qml @@ -0,0 +1,33 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../" as Overte + +ColumnLayout { + property alias text: labelItem.text + property alias model: comboItem.model + property alias textRole: comboItem.textRole + property alias valueRole: comboItem.valueRole + property alias currentIndex: comboItem.currentIndex + property alias enabled: comboItem.enabled + + id: item + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + spacing: 4 + + Overte.Label { + Layout.fillWidth: true + + id: labelItem + } + + Overte.ComboBox { + Layout.fillWidth: true + + id: comboItem + font.pixelSize: Overte.Theme.fontPixelSizeSmall + } +} diff --git a/interface/resources/qml/overte/settings/pages/Audio.qml b/interface/resources/qml/overte/settings/pages/Audio.qml new file mode 100644 index 00000000000..0d8c8affe1a --- /dev/null +++ b/interface/resources/qml/overte/settings/pages/Audio.qml @@ -0,0 +1,64 @@ +import "../../" as Overte +import "../" + +SettingsPage { + SwitchSetting { + text: "Mute Microphone" + + value: AudioScriptingInterface.muted + onValueChanged: AudioScriptingInterface.muted = value + } + + SwitchSetting { + text: "Push-to-Talk" + + value: AudioScriptingInterface.pushToTalk + onValueChanged: AudioScriptingInterface.pushToTalk = value + } + + SettingNote { + text: "Push [T] or squeeze both controller grips to talk." + } + + Header { text: qsTr("Audio Devices") } + + WideComboSetting { + text: "Output Device" + model: { + let tmp = []; + const source = AudioScriptingInterface.devices.output; + + for (let i = 0; i < source.rowCount(); i++) { + tmp.push(source.data(source.index(i, 0), /* DeviceNameRole */ 0x100)); + } + + return tmp; + } + + // TODO: how do these work??? + //currentIndex: 0 + //onCurrentIndexChanged: AudioScriptingInterface.setOutputDevice(currentIndex, false) + } + + WideComboSetting { + text: "Input Device" + model: { + let tmp = []; + const source = AudioScriptingInterface.devices.input; + + for (let i = 0; i < source.rowCount(); i++) { + tmp.push(source.data(source.index(i, 0), /* DeviceNameRole */ 0x100)); + } + + return tmp; + } + + // TODO: how do these work??? + //currentIndex: 0 + //onCurrentIndexChanged: AudioScriptingInterface.setInputDevice(currentIndex, false) + } + + SettingNote { + text: qsTr("Most VR runtimes will automatically switch the default audio devices to your headset.") + } +} diff --git a/interface/resources/qml/overte/settings/pages/Controls.qml b/interface/resources/qml/overte/settings/pages/Controls.qml new file mode 100644 index 00000000000..7f7a102d03a --- /dev/null +++ b/interface/resources/qml/overte/settings/pages/Controls.qml @@ -0,0 +1,199 @@ +import "../../" as Overte +import "../" + +// NOTE: There's a lot of "x depends on non-bindable properties" warnings, +// there's not much that can be done about them and I don't think there's +// a way of explicitly telling QML to not try binding to stuff. +SettingsPage { + Header { + text: qsTr("Desktop") + + // Hack to reduce the dead space on the top of the page + height: implicitHeight + } + + SwitchSetting { + id: invertMouseY + text: qsTr("Invert Y") + + value: MyAvatar.pitchSpeed < 0 + onValueChanged: { + MyAvatar.pitchSpeed = mouseSensitivity.value * (value ? -75 : 75); + } + } + + SliderSetting { + id: mouseSensitivity + text: qsTr("Mouse Sensitivity") + stepSize: 0.1 + from: 0.1 + to: 5.0 + valueToText: () => `${value.toLocaleString()}`; + + value: MyAvatar.yawSpeed / 75 + onValueChanged: { + MyAvatar.yawSpeed = value * 75; + MyAvatar.pitchSpeed = value * (invertMouseY.value ? -75 : 75); + } + } + + Header { text: qsTr("VR User") } + + SliderSetting { + text: qsTr("Height") + fineTweakButtons: true + stepSize: 0.01 + from: 0.8 + to: 2.5 + + valueToText: () => { + let meters = value.toLocaleString(); + let totalInches = Math.round(value * 39.37008); + + let feet = Math.floor(totalInches / 12); + let inches = totalInches % 12; + + return `${feet}'${inches}" ${meters.toLocaleString()}m`; + } + + value: MyAvatar.userHeight + onValueChanged: MyAvatar.userHeight = value + } + + ComboSetting { + text: qsTr("Dominant Hand") + model: [ + qsTr("Left"), + qsTr("Right"), + ] + + currentIndex: MyAvatar.getDominantHand() === "left" ? 0 : 1 + onCurrentIndexChanged: MyAvatar.setDominantHand(currentIndex === 0 ? "left" : "right") + } + + SwitchSetting { + text: qsTr("Seated Mode") + value: MyAvatar.standingMode !== 0 + onValueChanged: { + MyAvatar.standingMode = ( + value ? + 2 : // ForcedHeight + 0 // Standing + ); + } + } + + Header { text: qsTr("VR Movement") } + + SliderSetting { + text: qsTr("Turning Speed") + stepSize: 10 + from: 40 + to: 400 + valueToText: () => value < 50 ? qsTr("Snap turning") : `${value.toLocaleString()}`; + + value: MyAvatar.hmdYawSpeed + onValueChanged: { + MyAvatar.setSnapTurn(value < 50); + MyAvatar.HMDYawSpeed = value; + } + } + + SliderSetting { + id: walkingSpeed + text: qsTr("Walking Speed") + stepSize: 0.5 + from: 1.0 + to: 9 + valueToText: () => value < 1.5 ? qsTr("Teleport Only") : `${value.toLocaleString()} m/s`; + //enabled: !useAvatarDefaultWalkingSpeed.value + + value: MyAvatar.vrWalkSpeed + onValueChanged: { + if (value === 0.0) { + MyAvatar.useAdvancedMovementControls = false; + } else { + MyAvatar.useAdvancedMovementControls = true; + MyAvatar.vrWalkSpeed = value; + } + } + } + + ComboSetting { + text: qsTr("Movement Relative To") + enabled: walkingSpeed.value >= 1.5 + model: [ + qsTr("Head"), + qsTr("Hand"), + ] + + currentIndex: MyAvatar.getMovementReference() + onCurrentIndexChanged: MyAvatar.setMovementReference(currentIndex) + } + + /*SwitchSetting { + id: useAvatarDefaultWalkingSpeed + text: qsTr("Use equipped avatar's default walking speed if available") + + // TODO + value: false + }*/ + + Header { text: qsTr("VR UI") } + + ComboSetting { + text: qsTr("Virtual Keyboard Input") + model: [ + qsTr("Lasers"), + qsTr("Mallets"), + ] + + currentIndex: KeyboardScriptingInterface.preferMalletsOverLasers ? 1 : 0 + onCurrentIndexChanged: { + KeyboardScriptingInterface.preferMalletsOverLasers = currentIndex == 1; + } + } + + SliderSetting { + text: qsTr("Laser Smoothing Delay") + stepSize: 0.05 + from: 0.0 + to: 2.0 + valueToText: () => `${value.toLocaleString()}s`; + + value: PickScriptingInterface.handLaserDelay + onValueChanged: PickScriptingInterface.handLaserDelay = value + } + + // for later, once the gesture scripts are stable and merged + /* + Header { text: qsTr("VR Gestures") } + + SwitchSetting { + text: qsTr("Take Photo") + value: true + } + + SettingNote { + text: qsTr("Double-click the trigger on your dominant hand near your ear to take a photo, or hold the trigger to take an animated screenshot.") + } + + SwitchSetting { + text: qsTr("Laser Toggle") + value: true + } + + SettingNote { + text: qsTr("Click both triggers with your hands behind your head to toggle the interaction lasers.") + } + + SwitchSetting { + text: qsTr("Seated Mode Toggle") + value: true + } + + SettingNote { + text: qsTr("Double-tap the controller grip on your non-dominant hand near your ear to switch between seated and standing mode.") + } + */ +} diff --git a/interface/resources/qml/overte/settings/pages/General.qml b/interface/resources/qml/overte/settings/pages/General.qml new file mode 100644 index 00000000000..869aa43d88e --- /dev/null +++ b/interface/resources/qml/overte/settings/pages/General.qml @@ -0,0 +1,235 @@ +import "../../" as Overte +import "../" + +SettingsPage { + id: page + + Header { + text: qsTr("UI") + + // Hack to reduce the dead space on the top of the page + height: implicitHeight + } + + ComboSetting { + text: qsTr("Color Scheme") + model: [ + qsTr("Dark"), + qsTr("Light"), + qsTr("System"), + ] + + currentIndex: { + if (WindowScriptingInterface.useSystemColorScheme) { + return 2; + } else if (WindowScriptingInterface.darkMode) { + return 0; + } else { + return 1; + } + } + + onCurrentIndexChanged: { + switch (currentIndex) { + case 0: + WindowScriptingInterface.useSystemColorScheme = false; + WindowScriptingInterface.darkMode = true; + // FIXME: singletons can't access context properties? + Overte.Theme.darkMode = true; + break; + + case 1: + WindowScriptingInterface.useSystemColorScheme = false; + WindowScriptingInterface.darkMode = false; + // FIXME: singletons can't access context properties? + Overte.Theme.darkMode = false; + break; + + case 2: + WindowScriptingInterface.useSystemColorScheme = true; + WindowScriptingInterface.darkMode = true; + // FIXME: singletons can't access context properties? + Overte.Theme.darkMode = true; + break; + } + } + } + + ComboSetting { + text: qsTr("Contrast") + model: [ + qsTr("Standard"), + qsTr("High"), + qsTr("System"), + ] + + currentIndex: { + if (WindowScriptingInterface.useSystemContrast) { + return 2; + } else if (WindowScriptingInterface.highContrast) { + return 1; + } else { + return 0; + } + } + + onCurrentIndexChanged: { + switch (currentIndex) { + case 0: + WindowScriptingInterface.useSystemContrast = false; + WindowScriptingInterface.highContrast = false; + // FIXME: singletons can't access context properties? + Overte.Theme.highContrast = false; + break; + + case 1: + WindowScriptingInterface.useSystemContrast = false; + WindowScriptingInterface.highContrast = true; + // FIXME: singletons can't access context properties? + Overte.Theme.highContrast = true; + break; + + case 2: + WindowScriptingInterface.useSystemContrast = true; + WindowScriptingInterface.highContrast = false; + // FIXME: singletons can't access context properties? + Overte.Theme.highContrast = false; + break; + } + } + } + + SwitchSetting { + text: qsTr("Reduced Motion") + + value: WindowScriptingInterface.reducedMotion + onValueChanged: { + WindowScriptingInterface.reducedMotion = value; + + // FIXME: singletons can't access context properties? + Overte.Theme.reducedMotion = value; + } + } + + SettingNote { + text: qsTr("This setting will disable UI animations that slide or scale.") + } + + ComboSetting { + text: qsTr("Avatar Nametags") + textRole: "text" + valueRole: "value" + model: [ + { text: qsTr("Never "), value: "off" }, + { text: qsTr("When clicked"), value: "on" }, + { text: qsTr("Always"), value: "alwaysOn" }, + ] + + currentIndex: { + const setting = SettingsInterface.getValue("simplifiedNametag/avatarNametagMode", "on"); + switch (setting) { + case "off": return 0; + case "on": return 1; + case "alwaysOn": return 2; + } + } + + onCurrentIndexChanged: SettingsInterface.setValue("simplifiedNametag/avatarNametagMode", model[currentIndex].value) + } + + Header { text: qsTr("Screenshots") } + + FolderSetting { + text: qsTr("Folder") + value: Snapshot.getSnapshotsLocation() + onValueChanged: Snapshot.setSnapshotsLocation(value) + } + + ComboSetting { + text: qsTr("Format") + model: [ + "png", + "jpg", + "webp", + ] + + currentIndex: { + let current = Snapshot.getSnapshotFormat(); + for (let i in model) { + if (model[i] === current) { return i; } + } + return 0; + } + onCurrentIndexChanged: Snapshot.setSnapshotFormat(model[currentIndex]) + } + + SpinBoxSetting { + text: qsTr("Animation Duration") + from: 1 + to: 30 + + value: SettingsInterface.getValue("snapshotAnimatedDuration", 3) + onValueChanged: SettingsInterface.setValue("snapshotAnimatedDuration", value) + } + + SettingNote { + text: "Animated screenshots are saved as GIFs." + } + + Header { text: qsTr("Privacy") } + + SwitchSetting { + text: qsTr("Send Crash Reports") + + // FIXME: MenuScriptingInterface is super cursed, we shouldn't be relying on + // the *names* of the menu items to access them, especially once they're translated. + // Why are these even menu items in the first place? + value: MenuInterface.isOptionChecked("Enable Crash Reporting") + onValueChanged: MenuInterface.setIsOptionChecked("Enable Crash Reporting", value) + } + + SettingNote { + text: qsTr("Sending crash reports helps Overte development.") + } + + SwitchSetting { + text: qsTr("Discord Rich Presence") + value: SettingsInterface.getValue("useDiscordPresence", true) + onValueChanged: SettingsInterface.setValue("useDiscordPresence", value) + } + + SettingNote { + text: qsTr("If this setting is enabled, your current world will be shown on your Discord profile.") + } + + Header { text: qsTr("Legacy Features") } + + SettingNote { + text: qsTr("These settings are for legacy functionality that may be removed in a later release.") + } + + SliderSetting { + text: qsTr("VR Tablet Scale") + stepSize: 5 + from: 50 + to: 150 + valueToText: () => `${value}%` + + value: SettingsInterface.getValue("hmdTabletScale", 75) + onValueChanged: SettingsInterface.setValue("hmdTabletScale", value) + } + + ComboSetting { + text: qsTr("VR Tablet Input") + + // keep this in the same order as MyAvatar.TabletInputMode + model: [ + qsTr("Laser"), + qsTr("Stylus"), + qsTr("Finger Touch"), + ] + + currentIndex: MyAvatar.tabletInputMode + onCurrentIndexChanged: MyAvatar.tabletInputMode = currentIndex; + } +} diff --git a/interface/resources/qml/overte/settings/pages/Graphics.qml b/interface/resources/qml/overte/settings/pages/Graphics.qml new file mode 100644 index 00000000000..b37b00cf184 --- /dev/null +++ b/interface/resources/qml/overte/settings/pages/Graphics.qml @@ -0,0 +1,213 @@ +import "../../" as Overte +import "../" + +SettingsPage { + SliderSetting { + text: qsTr("Field of View") + stepSize: 5 + from: 20 + to: 130 + valueToText: () => `${value}°`; + + value: Render.getVerticalFieldOfView() + onValueChanged: Render.setVerticalFieldOfView(value) + } + + SliderSetting { + text: qsTr("Resolution Scale") + stepSize: 10 + from: 10 + to: 200 + valueToText: () => `${value}%`; + + value: Render.viewportResolutionScale * 100 + onValueChanged: Render.viewportResolutionScale = value / 100 + } + + ComboSetting { + enabled: deferredRendering.value + + text: qsTr("Anti-Aliasing") + + model: [ + // TODO: separate none and MSAA? + // RenderMainView.PreparePrimaryBufferForward.numSamples is a massive hack + deferredRendering.value ? qsTr("None") : qsTr("4x MSAA"), + qsTr("TAA"), + qsTr("FXAA"), + ] + + currentIndex: deferredRendering.value ? Render.getAntialiasingMode() : 0 + onCurrentIndexChanged: Render.setAntialiasingMode(currentIndex) + } + + ComboSetting { + text: qsTr("Level-of-Detail") + textRole: "text" + valueRole: "mode" + + model: [ + { text: qsTr("High"), mode: 0 }, + { text: qsTr("Medium"), mode: 1 }, + { text: qsTr("Low"), mode: 2 }, + ] + + currentIndex: LODManager.worldDetailQuality + onCurrentIndexChanged: LODManager.worldDetailQuality = model[currentIndex].mode + } + + Header { text: qsTr("Advanced") } + + SwitchSetting { + id: deferredRendering + text: qsTr("Deferred Rendering") + + value: Render.renderMethod === 0 + onValueChanged: Render.renderMethod = value ? 0 : 1 + } + + SettingNote { + text: qsTr("May affect performance, especially on mobile devices. Not compatible with MSAA.") + } + + SwitchSetting { + enabled: deferredRendering.value + text: qsTr("Shadows") + + value: Render.shadowsEnabled && deferredRendering.value + onValueChanged: Render.shadowsEnabled = value + } + + SwitchSetting { + enabled: deferredRendering.value + text: qsTr("Ambient Occlusion") + + value: Render.ambientOcclusionEnabled && deferredRendering.value + onValueChanged: Render.ambientOcclusionEnabled = value + } + + SwitchSetting { + text: qsTr("Local Lights") + + value: Render.localLightingEnabled + onValueChanged: Render.localLightingEnabled = value + } + + SwitchSetting { + text: qsTr("Bloom") + + value: Render.bloomEnabled + onValueChanged: Render.bloomEnabled = value + } + + SwitchSetting { + text: qsTr("Custom Shaders") + + value: Render.proceduralMaterialsEnabled + onValueChanged: Render.proceduralMaterialsEnabled = value + } + + SettingNote { + text: qsTr("Custom shaders are currently always unlit when deferred rendering is disabled.") + } + + SwitchSetting { + text: qsTr("Allow third-person camera to pass through walls") + + value: !Render.getCameraClippingEnabled() + onValueChanged: !Render.setCameraClippingEnabled(value) + } + + Header { text: qsTr("Desktop Window") } + + ComboSetting { + text: qsTr("Fullscreen Monitor") + model: Render.getScreens() + + currentIndex: { + const index = model.indexOf(Render.getFullScreenScreen()); + return index !== -1 ? index : 0; + } + onCurrentIndexChanged: Render.setFullScreenScreen(model[currentIndex]) + } + + ComboSetting { + id: fpsLimit + text: qsTr("Framerate Limit") + model: [ + // see Performance.RefreshRateProfile + qsTr("20 FPS"), + qsTr("30 FPS"), + qsTr("60 FPS"), + qsTr("Custom"), + ] + + currentIndex: Performance.getRefreshRateProfile() + onCurrentIndexChanged: Performance.setRefreshRateProfile(currentIndex) + } + + SettingNote { + text: qsTr("Higher settings may increase battery usage. VR is always run at your headset's native framerate when possible.") + } + + Header { + text: qsTr("Custom Framerate Limit") + visible: fpsLimit.currentIndex === 3 + } + + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Focused Active") + from: 5 + to: 500 + value: Performance.getCustomRefreshRate(0 /* FOCUS_ACTIVE */) + onValueChanged: Performance.setCustomRefreshRate(0 /* FOCUS_ACTIVE */, value) + } + + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Focused AFK") + from: 1 + to: 500 + value: Performance.getCustomRefreshRate(1 /* FOCUS_INACTIVE */) + onValueChanged: Performance.setCustomRefreshRate(1 /* FOCUS_INACTIVE */, value) + } + + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Unfocused") + from: 1 + to: 500 + value: Performance.getCustomRefreshRate(2 /* UNFOCUS */) + onValueChanged: Performance.setCustomRefreshRate(2 /* FOCUS */, value) + } + + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Minimized") + from: 1 + to: 500 + value: Performance.getCustomRefreshRate(3 /* MINIMIZED */) + onValueChanged: Performance.setCustomRefreshRate(3 /* MINIMIZED */, value) + } + + // FIXME: Does anybody actually care about these? Are they useful? + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Startup") + from: 5 + to: 500 + value: Performance.getCustomRefreshRate(4 /* STARTUP */) + onValueChanged: Performance.setCustomRefreshRate(4 /* STARTUP */, value) + } + + SpinBoxSetting { + visible: fpsLimit.currentIndex === 3 + text: qsTr("Shutdown") + from: 5 + to: 500 + value: Performance.getCustomRefreshRate(5 /* SHUTDOWN */) + onValueChanged: Performance.setCustomRefreshRate(5 /* SHUTDOWN */, value) + } +} + diff --git a/interface/resources/qml/overte/settings/pages/qmldir b/interface/resources/qml/overte/settings/pages/qmldir new file mode 100644 index 00000000000..db33df213d5 --- /dev/null +++ b/interface/resources/qml/overte/settings/pages/qmldir @@ -0,0 +1,5 @@ +module SettingsPages +General 1.0 General.qml +Graphics 1.0 Graphics.qml +Controls 1.0 Controls.qml +Audio 1.0 Audio.qml diff --git a/interface/resources/qml/overte/settings/qmldir b/interface/resources/qml/overte/settings/qmldir new file mode 100644 index 00000000000..88909ca8660 --- /dev/null +++ b/interface/resources/qml/overte/settings/qmldir @@ -0,0 +1,11 @@ +module Settings +Settings 1.0 Settings.qml +SettingsPage 1.0 SettingsPage.qml +SettingNote 1.0 SettingNote.qml +Header 1.0 Header.qml +SwitchSetting 1.0 SwitchSetting.qml +FolderSetting 1.0 FolderSetting.qml +ComboSetting 1.0 ComboSetting.qml +WideComboSetting 1.0 WideComboSetting.qml +SpinBoxSetting 1.0 SpinBoxSetting.qml +SliderSetting 1.0 SliderSetting.qml diff --git a/interface/resources/qml/overte/staging/MediaPlayer.qml b/interface/resources/qml/overte/staging/MediaPlayer.qml new file mode 100644 index 00000000000..0e4a6c39a14 --- /dev/null +++ b/interface/resources/qml/overte/staging/MediaPlayer.qml @@ -0,0 +1,386 @@ +import QtQuick +import QtQuick.Layouts +import QtMultimedia + +import ".." as Overte + +Rectangle { + property url source: "file:///home/ada/var/livestream/clips/corrupted-seagull.mp4" + + anchors.fill: parent + + id: root + radius: 8 + color: Qt.darker(Overte.Theme.paletteActive.base) + border.width: Overte.Theme.borderWidth + border.color: Overte.Theme.paletteActive.base + + implicitWidth: 854 + implicitHeight: 480 + + MediaPlayer { + id: mediaPlayer + source: root.source + audioOutput: audioOutput + videoOutput: videoOutput + } + + AudioOutput { + id: audioOutput + volume: volumeSlider.value + } + + VideoOutput { + anchors.fill: parent + anchors.margins: root.border.width + + id: videoOutput + } + + Item { + id: controlsOverlay + anchors.fill: root + anchors.margins: root.border.width + opacity: 0.0 + + transitions: Transition { + NumberAnimation { + properties: "opacity" + easing.type: Easing.OutExpo + duration: 500 + } + } + + states: [ + State { + name: "idle" + when: !hoverArea.hovered + + PropertyChanges { + target: controlsOverlay + opacity: 0.0 + } + }, + State { + name: "hovered" + when: hoverArea.hovered + + PropertyChanges { + target: controlsOverlay + opacity: 1.0 + } + } + ] + + HoverHandler { + id: hoverArea + target: parent + } + + Rectangle { + anchors.top: controlsOverlay.top + anchors.left: controlsOverlay.left + anchors.margins: 8 + width: childrenRect.width + height: childrenRect.height + radius: 8 + color: Overte.Theme.highContrast ? "black" : "#80000000" + visible: titleLabel.text !== "" || statusLabel.text !== "" + + ColumnLayout { + id: statusColumn + + Overte.Label { + Layout.margins: 8 + + id: titleLabel + visible: text !== "" + + // metadata title > extracted filename > raw url + text: { + const metaTitle = mediaPlayer.metaData.value(MediaMetaData.Title); + + if (metaTitle) { + return metaTitle; + } else { + const file = new URL(root.source).pathname.replace(/^.+?([^\/]+?)(?:\..+)?$/, "$1") + + if (file) { + return file; + } else { + return root.source; + } + } + } + } + + Overte.Label { + Layout.margins: 8 + + id: statusLabel + visible: text !== "" + + text: { + switch (mediaPlayer.mediaStatus) { + case MediaPlayer.LoadingMedia: + return qsTr("Loading…"); + + case MediaPlayer.BufferingMedia: + return qsTr("Buffering…"); + + case MediaPlayer.StalledMedia: + return qsTr("Stalled! Connection may have been lost."); + + case MediaPlayer.InvalidMedia: + return qsTr("Media file is not playable."); + + default: return ""; + } + } + } + } + } + + Overte.RoundButton { + anchors.top: controlsOverlay.top + anchors.right: controlsOverlay.right + anchors.margins: 8 + + icon.color: Overte.Theme.paletteActive.buttonText + icon.source: "../icons/info.svg" + icon.width: 24 + icon.height: 24 + + id: infoButton + checkable: true + } + + Rectangle { + anchors.right: infoButton.left + anchors.top: infoButton.top + anchors.rightMargin: 8 + radius: 8 + color: Overte.Theme.highContrast ? "black" : "#80000000" + visible: infoButton.checked + + width: Math.min(controlsOverlay.width / 2, metadataColumn.implicitWidth + 16) + height: metadataColumn.implicitHeight + 16 + + ColumnLayout { + id: metadataColumn + anchors.fill: parent + anchors.margins: 8 + + Overte.Label { + Layout.fillWidth: true + font.family: Overte.Theme.monoFontFamily + font.pixelSize: Overte.Theme.fontPixelSizeSmall + elide: Text.ElideMiddle + text: root.source + } + + Overte.Label { + Layout.fillWidth: true + text: { + const containerNames = new Map([ + [0, "WMV"], + [1, "AVI"], + [2, "Matroska (mkv)"], + [3, "MPEG-4 (mp4)"], + [4, "Ogg"], + [5, "QuickTime (mov)"], + [6, "WebM"], + [7, "MPEG-4 Audio"], + [8, "AAC"], + [9, "WMA"], + [10, "MP3"], + [11, "FLAC"], + [12, "WAV"], + ]); + const vcodecNames = new Map([ + [0, "MPEG-1"], + [1, "MPEG-2"], + [2, "MPEG-4"], + [3, "H.264"], + [4, "H.265"], + [5, "VP8"], + [6, "VP9"], + [7, "AV1"], + [8, "Theora"], + [9, "WMV Video"], + [10, "Motion JPEG"], + ]); + const acodecNames = new Map([ + [0, "MP3"], + [1, "AAC"], + [2, "Dolby AC3"], + [3, "Dolby EAC3"], + [4, "FLAC"], + [5, "Dolby TrueHD"], + [6, "Opus"], + [7, "Ogg Vorbis"], + [8, "WAV"], + [9, "WMA"], + [10, "ALAC"], + ]); + let chunks = []; + + if ( + mediaPlayer.mediaStatus === MediaPlayer.NoMedia || + mediaPlayer.mediaStatus === MediaPlayer.InvalidMedia + ) { + return qsTr("Unloaded"); + } + + const containerName = containerNames.get(mediaPlayer.metaData.value(MediaMetaData.FileFormat)); + chunks.push(`${qsTr("Container:")} ${containerName ?? qsTr("Other")}`); + + if (mediaPlayer.hasVideo) { + const track = mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack]; + const codecName = vcodecNames.get(track.value(MediaMetaData.VideoCodec)); + const fps = track.value(MediaMetaData.VideoFrameRate); + const resolution = track.value(MediaMetaData.Resolution); + chunks.push(`${qsTr("Video:")} ${resolution.width}x${resolution.height}@${fps.toFixed(2)} ${codecName ?? qsTr("Other")}`); + } + + if (mediaPlayer.hasAudio) { + const track = mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack]; + const codecName = acodecNames.get(track.value(MediaMetaData.AudioCodec)); + const bitrate = Math.round(track.value(MediaMetaData.AudioBitRate) / 1000); + chunks.push(`${qsTr("Audio:")} ${bitrate} kbps ${codecName ?? qsTr("Other")}`); + } + + return chunks.join("\n"); + } + } + } + } + + Rectangle { + anchors.left: controlsOverlay.left + anchors.right: controlsOverlay.right + anchors.bottom: controlsOverlay.bottom + anchors.margins: 8 + height: mediaControlsLayout.implicitHeight + 16 + radius: 16 + color: Overte.Theme.highContrast ? "black" : "#80000000" + + RowLayout { + anchors.fill: parent + anchors.margins: 8 + id: mediaControlsLayout + + Overte.RoundButton { + implicitWidth: 48 + implicitHeight: 48 + icon.source: ( + mediaPlayer.playbackState === MediaPlayer.PlayingState ? + "../icons/pause.svg" : + "../icons/triangle_right.svg" + ) + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 32 + icon.height: 32 + + onClicked: { + if (mediaPlayer.playbackState === MediaPlayer.PlayingState) { + mediaPlayer.pause(); + } else { + mediaPlayer.play(); + } + } + } + + ColumnLayout { + RowLayout { + Overte.Label { + font.family: Overte.Theme.monoFontFamily + text: { + const totalSeconds = Math.floor(mediaPlayer.position / 1000); + + const seconds = (totalSeconds % 60).toString().padStart(2, "0"); + const minutes = (Math.floor(totalSeconds / 60) % 60).toString().padStart(2, "0"); + const hours = Math.floor(totalSeconds / 60 / 60).toString().padStart(2, "0"); + + return `${hours}:${minutes}:${seconds}`; + } + } + + Item { Layout.fillWidth: true } + + Overte.Label { + font.family: Overte.Theme.monoFontFamily + text: { + const totalSeconds = Math.floor(mediaPlayer.duration / 1000); + + const seconds = (totalSeconds % 60).toString().padStart(2, "0"); + const minutes = (Math.floor(totalSeconds / 60) % 60).toString().padStart(2, "0"); + const hours = Math.floor(totalSeconds / 60 / 60).toString().padStart(2, "0"); + + return `${hours}:${minutes}:${seconds}`; + } + } + } + + RowLayout { + Overte.RoundButton { + icon.source: "../icons/skip_backward.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + onClicked: mediaPlayer.position -= 5000 + } + + Overte.Slider { + Layout.fillWidth: true + value: mediaPlayer.position / mediaPlayer.duration + + onMoved: { + mediaPlayer.position = value * mediaPlayer.duration; + } + } + + Overte.RoundButton { + icon.source: "../icons/skip_forward.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + onClicked: mediaPlayer.position += 5000 + } + } + } + + Overte.RoundButton { + id: muteButton + icon.source: { + if (audioOutput.muted) { + return "../icons/speaker_muted.svg"; + } else if (audioOutput.volume === 0) { + return "../icons/speaker_inactive.svg" + } else { + return "../icons/speaker_active.svg"; + } + } + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 24 + icon.height: 24 + checkable: true + checked: audioOutput.muted + + onClicked: audioOutput.muted = !audioOutput.muted; + } + + Overte.Slider { + Layout.preferredWidth: 96 + + id: volumeSlider + value: 0.75 + + onValueChanged: { + audioOutput.muted = false; + } + } + } + } + } +} diff --git a/interface/resources/qml/overte/staging/Node.qml b/interface/resources/qml/overte/staging/Node.qml new file mode 100644 index 00000000000..3c8a07ba543 --- /dev/null +++ b/interface/resources/qml/overte/staging/Node.qml @@ -0,0 +1,181 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects + +import ".." as Overte + +Rectangle { + property color titleColor: "#a000a0" + property string title: "Error" + property string bodyText: "" + property string helpText: "" + + property list inputs: [] + property list outputs: [] + + DragHandler { + dragThreshold: Overte.Theme.fontPixelSize + cursorShape: Qt.DragMoveCursor + } + + width: { + let accum = 0; + + accum += titleLayout.implicitWidth; + + return accum; + } + + height: { + let accum = 0; + + if (title !== "") { accum += titlebar.height + 4; } + + let inputsAccum = 0, outputsAccum = 0; + for (const input of inputs) { inputsAccum += 24 + 4; } + for (const output of outputs) { outputsAccum += 24 + 4; } + accum += Math.max(inputsAccum, outputsAccum) + 4; + + return accum; + } + + id: control + radius: Overte.Theme.borderRadius + topLeftRadius: control.radius * 3 + topRightRadius: control.radius * 3 + + border.width: Overte.Theme.borderWidth + border.color: ( + Overte.Theme.highContrast ? + Overte.Theme.paletteActive.buttonText : + Qt.darker(Overte.Theme.paletteActive.base, Overte.Theme.borderDarker) + ) + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(Overte.Theme.paletteActive.base, 1.1) + } + GradientStop { + position: 0.5; color: Overte.Theme.paletteActive.base + } + GradientStop { + position: 1.0; color: Qt.darker(Overte.Theme.paletteActive.base, 1.1) + } + } + + Rectangle { + anchors.fill: titleLayout + visible: control.title !== "" + id: titlebar + + topLeftRadius: control.radius * 2 + topRightRadius: control.radius * 2 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.lighter(control.titleColor, 1.1) + } + GradientStop { + position: 0.2 + color: Qt.darker(control.titleColor, 1.1) + } + GradientStop { + position: 0.8 + color: control.titleColor + } + GradientStop { + position: 1.0 + color: Qt.darker(control.titleColor, 1.3) + } + } + } + + RowLayout { + anchors.left: control.left + anchors.right: control.right + anchors.top: control.top + anchors.margins: Overte.Theme.borderWidth + + id: titleLayout + spacing: 4 + + Overte.Label { + Layout.margins: 6 + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: Overte.Theme.fontPixelSize * 1.5 + visible: control.title !== "" + verticalAlignment: Text.AlignVCenter + text: control.title + id: titleText + color: "white" + + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowVerticalOffset: 2 + shadowHorizontalOffset: 2 + shadowColor: Qt.darker(control.titleColor, 2.0) + shadowBlur: 0.2 + } + } + + Overte.RoundButton { + Layout.margins: 4 + Layout.alignment: Qt.AlignCenter + visible: control.helpText !== "" + implicitWidth: Overte.Theme.fontPixelSize + 8 + implicitHeight: Overte.Theme.fontPixelSize + 8 + text: "?" + } + } + + Column { + anchors.left: control.left + anchors.top: titlebar.bottom + anchors.topMargin: 4 + spacing: 4 + id: leftPlugs + + Repeater { + model: control.inputs + delegate: NodePlug { + required property string type + + x: -10 + color: Overte.Theme.paletteActive.scriptTypeColors[type] ?? "#ff00ff" + } + } + } + + Overte.Label { + anchors.margins: 8 + anchors.bottom: control.bottom + anchors.left: leftPlugs.right + anchors.right: rightPlugs.left + anchors.top: control.title !== "" ? titlebar.bottom : control.top + visible: control.bodyText !== "" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: control.height / 2 + text: control.bodyText + } + + Column { + anchors.right: control.right + anchors.top: titlebar.bottom + anchors.topMargin: 4 + spacing: 4 + id: rightPlugs + + Repeater { + model: control.outputs + delegate: NodePlug { + required property string type + + x: 10 + color: Overte.Theme.paletteActive.scriptTypeColors[type] ?? "#ff00ff" + } + } + } +} diff --git a/interface/resources/qml/overte/staging/NodeGraph.qml b/interface/resources/qml/overte/staging/NodeGraph.qml new file mode 100644 index 00000000000..974fdcbff60 --- /dev/null +++ b/interface/resources/qml/overte/staging/NodeGraph.qml @@ -0,0 +1,29 @@ +import QtQuick + +import ".." as Overte +import "." + +Rectangle { + id: nodeGraph + color: Qt.darker(Overte.Theme.paletteActive.base, 1.1) + + Overte.Switch { + onToggled: Overte.Theme.darkMode = checked + } + + Node { + x: 64 + y: 64 + + titleColor: "#00a000" + title: qsTr("Flush Entity Properties") + helpText: qsTr("Commits all changes to an entity's properties at once.") + inputs: [ + { type: "exec" }, + { type: "entity" }, + ] + outputs: [ + { type: "exec" }, + ] + } +} diff --git a/interface/resources/qml/overte/staging/NodePlug.qml b/interface/resources/qml/overte/staging/NodePlug.qml new file mode 100644 index 00000000000..d80277d3593 --- /dev/null +++ b/interface/resources/qml/overte/staging/NodePlug.qml @@ -0,0 +1,52 @@ +import QtQuick + +import ".." as Overte + +Rectangle { + property bool active: false + + implicitWidth: 24 + implicitHeight: 24 + + id: plug + radius: height / 2 + border.width: Overte.Theme.borderWidth + border.color: Qt.darker(color, Overte.Theme.borderDarker) + color: "#a000a0" + + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.lighter(plug.color, 1.3) + } + GradientStop { + position: 0.5 + color: plug.color + } + GradientStop { + position: 1.0 + color: Qt.darker(plug.color, 1.3) + } + } + + Rectangle { + anchors.fill: plug + anchors.margins: plug.height / 5 + radius: height / 2 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.lighter(plug.color, plug.active ? 2.5 : 0.6) + } + GradientStop { + position: 0.5 + color: Qt.lighter(plug.color, plug.active ? 1.5 : 0.7) + } + GradientStop { + position: 1.0 + color: Qt.lighter(plug.color, plug.active ? 1.0 : 0.8) + } + } + } +} diff --git a/interface/resources/qml/overte/staging/README.md b/interface/resources/qml/overte/staging/README.md new file mode 100644 index 00000000000..05b0f5676c1 --- /dev/null +++ b/interface/resources/qml/overte/staging/README.md @@ -0,0 +1 @@ +Stuff in this folder isn't ready yet. They're here so they can be picked up later. diff --git a/interface/resources/qml/overte/staging/desktop/AppToolbar.qml b/interface/resources/qml/overte/staging/desktop/AppToolbar.qml new file mode 100644 index 00000000000..07a0b1b5d3e --- /dev/null +++ b/interface/resources/qml/overte/staging/desktop/AppToolbar.qml @@ -0,0 +1,82 @@ +import QtQuick +import QtQuick.Controls + +import ".." as Overte + +Item { + readonly property int buttonSize: 80 + + id: root + implicitHeight: buttonSize + (buttonList.anchors.margins * 2) + Overte.Theme.scrollbarWidth + 4 + implicitWidth: 8 * (buttonSize + buttonList.spacing) + buttonList.anchors.margins + + Rectangle { + anchors.fill: root + radius: Overte.Theme.borderRadius + color: Overte.Theme.paletteActive.dialogShade + } + + ListView { + anchors.fill: parent + anchors.margins: 4 + + id: buttonList + orientation: Qt.Horizontal + spacing: 4 + clip: true + + ScrollBar.horizontal: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + } + + model: [ + {name: "Mute", iconSource: "../icons/speaker_muted.svg", checkable: true}, + {name: "Settings", iconSource: "../icons/settings_cog.svg", checkable: true}, + {name: "Contacts", iconSource: "../icons/add_friend.svg", checkable: true}, + {name: "Body Paint", iconSource: "../icons/pencil.svg", checkable: true}, + {name: "Eyes", iconSource: "../icons/eye_open.svg", checkable: false}, + + {name: "Star", iconSource: "../icons/gold_star.svg", checkable: false}, + {name: "Star", iconSource: "../icons/gold_star.svg", checkable: false}, + {name: "Star", iconSource: "../icons/gold_star.svg", checkable: false}, + {name: "Star", iconSource: "../icons/gold_star.svg", checkable: false}, + {name: "Star", iconSource: "../icons/gold_star.svg", checkable: false}, + ] + delegate: Overte.Button { + required property url iconSource + required property string name + required checkable + + implicitWidth: buttonSize + implicitHeight: buttonSize + + Image { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: buttonLabel.top + anchors.margins: 2 + + source: iconSource + sourceSize.width: width + sourceSize.height: height + + fillMode: Image.PreserveAspectFit + } + + Overte.Label { + id: buttonLabel + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 2 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignBottom + text: name + + font.pixelSize: Overte.Theme.fontPixelSizeSmall + } + } + } +} diff --git a/interface/resources/qml/overte/staging/desktop/Desktop.qml b/interface/resources/qml/overte/staging/desktop/Desktop.qml new file mode 100644 index 00000000000..00e52b1085d --- /dev/null +++ b/interface/resources/qml/overte/staging/desktop/Desktop.qml @@ -0,0 +1,70 @@ +import QtQuick + +import ".." as Overte +import "." + +//Item { +Rectangle { + color: "#303030" + + anchors.fill: parent + implicitWidth: 800 + implicitHeight: 600 + + id: desktopRoot + + Item { + id: toolbarContainer + anchors.horizontalCenter: desktopRoot.horizontalCenter + anchors.bottom: toolbarToggleButton.top + anchors.margins: 4 + height: appToolbar.height + + AppToolbar { + id: appToolbar + x: -width / 2 + y: Overte.Theme.reducedMotion ? 0 : height * 2 + opacity: 0.0 + + states: State { + name: "open" + when: toolbarToggleButton.checked + PropertyChanges { + target: appToolbar + y: 0 + opacity: 1.0 + } + } + + transitions: Transition { + reversible: true + + NumberAnimation { + properties: "y" + easing.type: Easing.OutExpo + duration: 500 + } + + NumberAnimation { + properties: "opacity" + easing.type: Easing.OutExpo + duration: 500 + } + } + } + } + + Overte.RoundButton { + anchors.horizontalCenter: desktopRoot.horizontalCenter + anchors.bottom: desktopRoot.bottom + anchors.margins: 16 + focusPolicy: Qt.NoFocus + + id: toolbarToggleButton + checkable: true + icon.source: checked ? "../icons/triangle_down.svg" : "../icons/triangle_up.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + } +} diff --git a/interface/resources/qml/overte/staging/desktop/qmldir b/interface/resources/qml/overte/staging/desktop/qmldir new file mode 100644 index 00000000000..5621c982de8 --- /dev/null +++ b/interface/resources/qml/overte/staging/desktop/qmldir @@ -0,0 +1,3 @@ +module Desktop +Desktop 1.0 Desktop.qml +AppToolbar 1.0 AppToolbar.qml diff --git a/interface/resources/qml/overte/staging/keyboard/Keyboard.qml b/interface/resources/qml/overte/staging/keyboard/Keyboard.qml new file mode 100644 index 00000000000..ce92428a7d7 --- /dev/null +++ b/interface/resources/qml/overte/staging/keyboard/Keyboard.qml @@ -0,0 +1,192 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte +import "." as OverteKeyboard + +Rectangle { + id: keyboardRoot + color: Overte.Theme.paletteActive.base + border.color: Qt.lighter(color) + border.width: 2 + radius: 12 + width: 1300 + height: 380 + + PropertyAnimation on opacity { + from: 0 + to: 1 + duration: 500 + } + + // TODO: replace this with Keybaord.layout or something + Component.onCompleted: { + const xhr = new XMLHttpRequest; + xhr.open("GET", "../../keyboard_ansi.json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + buildLayout(JSON.parse(xhr.responseText)); + } + }; + xhr.send(); + } + + function keyPressed(key) { + // TODO + print(`Press ${key.legend}, ${key.unshifted_keycode}`); + + if (key.unshifted_char !== "") { + modifiers = 0; + } + } + + function keyReleased(key) { + // TODO + print(`Release ${key.legend}, ${key.unshifted_keycode}`); + + switch (key.unshifted_keycode) { + case Qt.Key_Shift: modifiers ^= Qt.ShiftModifier; break; + case Qt.Key_Control: modifiers ^= Qt.ControlModifier; break; + case Qt.Key_Alt: modifiers ^= Qt.AltModifier; break; + case Qt.Key_Meta: modifiers ^= Qt.MetaModifier; break; + } + } + + property int modifiers: 0 + + Component { + id: columnLayout + ColumnLayout { + property real horizontalSpan: 1.0 + property real verticalSpan: 1.0 + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.horizontalStretchFactor: Math.round(horizontalSpan * 1000) + Layout.verticalStretchFactor: Math.round(verticalSpan * 1000) + } + } + Component { + id: rowLayout + RowLayout { + property real horizontalSpan: 1.0 + property real verticalSpan: 1.0 + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.horizontalStretchFactor: Math.round(horizontalSpan * 1000) + Layout.verticalStretchFactor: Math.round(verticalSpan * 1000) + } + } + Component { + id: item + + Item { + property real horizontalSpan: 1.0 + property real verticalSpan: 1.0 + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.horizontalStretchFactor: Math.round(horizontalSpan * 1000) + Layout.verticalStretchFactor: Math.round(verticalSpan * 1000) + implicitWidth: 1 + implicitHeight: 1 + } + } + Component { + id: keyboardKey + KeyboardKey { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.horizontalStretchFactor: Math.round(span * 1000) + implicitWidth: 1 + implicitHeight: 1 + } + } + + function buildLayout(layout) { + for (const topColumn of layout) { + let topColumnSpan = 1.0; + if (!isNaN(topColumn[0])) { + topColumnSpan = topColumn[0]; + } + + const topColumnItem = columnLayout.createObject(layoutRoot, { + horizontalSpan: topColumnSpan, + }); + + for (const row of topColumn) { + // the span width we handled earlier + if (!isNaN(parseFloat(row))) { continue; } + + // vertical spacer + if (!Array.isArray(row)) { + item.createObject(topColumnItem, { + verticalSpan: row.verticalSpan ?? 1.0, + }); + continue; + } + + const rowItem = rowLayout.createObject(topColumnItem); + + for (const key of row) { + if (key.legend) { + const o = keyboardKey.createObject(rowItem, { + unshifted_keycode: Qt[key.keycode ?? key.unshifted_keycode] ?? 0, + shifted_keycode: Qt[key.keycode ?? key.shifted_keycode] ?? 0, + unshifted_char: key.unshifted_char ?? "", + shifted_char: key.shifted_char ?? "", + legend: key.legend ?? "", + span: key.span ?? 1.0, + }); + + if ( + o.unshifted_keycode === Qt.Key_Insert || + o.unshifted_keycode === Qt.Key_Home || + o.unshifted_keycode === Qt.Key_PageUp || + o.unshifted_keycode === Qt.Key_Delete || + o.unshifted_keycode === Qt.Key_End || + o.unshifted_keycode === Qt.Key_PageDown + ) { + o.font.pixelSize = Overte.Theme.fontPixelSizeSmall; + } + + if ( + o.unshifted_keycode === Qt.Key_CapsLock || + o.unshifted_keycode === Qt.Key_Shift || + o.unshifted_keycode === Qt.Key_Control || + o.unshifted_keycode === Qt.Key_Alt || + o.unshifted_keycode === Qt.Key_Meta + ) { + o.checkable = true; + } + } else { + // horizontal spacer + item.createObject(rowItem, { + horizontalSpan: key.span ?? 1.0, + }); + } + } + } + } + } + + RowLayout { + id: layoutRoot + anchors.fill: parent + anchors.margins: 12 + } + + Overte.RoundButton { + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 6 + horizontalPadding: 2 + verticalPadding: 2 + width: 40 + height: 40 + + backgroundColor: Overte.Theme.paletteActive.buttonDestructive + text: "🗙" + } +} diff --git a/interface/resources/qml/overte/staging/keyboard/KeyboardKey.qml b/interface/resources/qml/overte/staging/keyboard/KeyboardKey.qml new file mode 100644 index 00000000000..9aaa7e7b6bb --- /dev/null +++ b/interface/resources/qml/overte/staging/keyboard/KeyboardKey.qml @@ -0,0 +1,82 @@ +import QtQuick + +import "../" + +Button { + property int unshifted_keycode: 0 + property int shifted_keycode: 0 + + property string unshifted_char: "" + property string shifted_char: "" + + property string legend: "" + property real span: 1.0 + + id: control + text: legend + focusPolicy: Qt.NoFocus + horizontalPadding: 4 + verticalPadding: 4 + + onPressed: keyboardRoot.keyPressed(this) + onReleased: keyboardRoot.keyReleased(this) + + Connections { + target: keyboardRoot + + function onModifiersChanged() { + const value = keyboardRoot.modifiers; + + switch (unshifted_keycode) { + case Qt.Key_Shift: + checked = (value & Qt.ShiftModifier) !== 0; + break; + + case Qt.Key_Control: + checked = (value & Qt.ControlModifier) !== 0; + break; + + case Qt.Key_Alt: + checked = (value & Qt.AltModifier) !== 0; + break; + + case Qt.Key_Meta: + checked = (value & Qt.MetaModifier) !== 0; + break; + } + } + } + + contentItem: Text { + text: control.text + font.family: control.font.family + font.pixelSize: control.height / 3 + color: Theme.paletteActive.buttonText + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideNone + } + + background: Rectangle { + id: controlBg + radius: Theme.borderRadius + border.width: Theme.borderWidth + border.color: control.checked ? + Theme.paletteActive.focusRing : + (Theme.highContrast ? Theme.paletteActive.controlText : Qt.darker(control.backgroundColor, Theme.borderDarker)) + color: (control.down || control.checked) ? Qt.darker(control.backgroundColor, Theme.checkedDarker) : + control.hovered ? Qt.lighter(control.backgroundColor, Theme.hoverLighter) : + control.backgroundColor; + gradient: Gradient { + GradientStop { + position: 0.0; color: Qt.lighter(controlBg.color, (control.down || control.checked) ? 0.9 : 1.1) + } + GradientStop { + position: 0.5; color: controlBg.color + } + GradientStop { + position: 1.0; color: Qt.darker(controlBg.color, (control.down || control.checked) ? 0.9 : 1.1) + } + } + } +} diff --git a/interface/resources/qml/overte/staging/keyboard/keyboard_ansi.json b/interface/resources/qml/overte/staging/keyboard/keyboard_ansi.json new file mode 100644 index 00000000000..e812db21438 --- /dev/null +++ b/interface/resources/qml/overte/staging/keyboard/keyboard_ansi.json @@ -0,0 +1,506 @@ +[ + [ + [ + { + "legend": "Esc", + "keycode": "Key_Escape" + }, + { "span": 0.25 }, + { + "legend": "F1", + "keycode": "Key_F1" + }, + { + "legend": "F2", + "keycode": "Key_F2" + }, + { + "legend": "F3", + "keycode": "Key_F3" + }, + { + "legend": "F4", + "keycode": "Key_F4" + }, + { "span": 0.25 }, + { + "legend": "F5", + "keycode": "Key_F5" + }, + { + "legend": "F6", + "keycode": "Key_F6" + }, + { + "legend": "F7", + "keycode": "Key_F7" + }, + { + "legend": "F8", + "keycode": "Key_F8" + }, + { "span": 0.25 }, + { + "legend": "F9", + "keycode": "Key_F9" + }, + { + "legend": "F10", + "keycode": "Key_F10" + }, + { + "legend": "F11", + "keycode": "Key_F11" + }, + { + "legend": "F12", + "keycode": "Key_F12" + } + ], + { "verticalSpan": 0.003 }, + [ + { + "legend": "`\n~", + "unshifted_keycode": "Key_QuoteLeft", + "shifted_keycode": "Key_Tilde", + "unshifted_char": "`", + "shifted_char": "~" + }, + { + "legend": "!\n1", + "unshifted_keycode": "Key_1", + "shifted_keycode": "Key_Exclam", + "unshifted_char": "1", + "shifted_char": "!" + }, + { + "legend": "@\n2", + "unshifted_keycode": "Key_2", + "shifted_keycode": "Key_At", + "unshifted_char": "2", + "shifted_char": "@" + }, + { + "legend": "#\n3", + "unshifted_keycode": "Key_3", + "shifted_keycode": "Key_NumberSign", + "unshifted_char": "3", + "shifted_char": "#" + }, + { + "legend": "$\n4", + "unshifted_keycode": "Key_4", + "shifted_keycode": "Key_Dollar", + "unshifted_char": "4", + "shifted_char": "#" + }, + { + "legend": "%\n5", + "unshifted_keycode": "Key_5", + "shifted_keycode": "Key_Percent", + "unshifted_char": "5", + "shifted_char": "%" + }, + { + "legend": "^\n6", + "unshifted_keycode": "Key_6", + "shifted_keycode": "Key_AsciiCircum", + "unshifted_char": "6", + "shifted_char": "^" + }, + { + "legend": "&\n7", + "unshifted_keycode": "Key_7", + "shifted_keycode": "Key_Ampersand", + "unshifted_char": "7", + "shifted_char": "&" + }, + { + "legend": "*\n8", + "unshifted_keycode": "Key_8", + "shifted_keycode": "Key_Asterisk", + "unshifted_char": "8", + "shifted_char": "*" + }, + { + "legend": "(\n9", + "unshifted_keycode": "Key_9", + "shifted_keycode": "Key_ParenLeft", + "unshifted_key": "9", + "shifted_key": "(" + }, + { + "legend": ")\n0", + "unshifted_keycode": "Key_0", + "shifted_keycode": "Key_ParentRight", + "unshifted_key": "0", + "shifted_key": ")" + }, + { + "legend": "_\n-", + "unshifted_keycode": "Key_Minus", + "shifted_keycode": "Key_Underscore", + "unshifted_char": "-", + "shifted_char": "_" + }, + { + "legend": "+\n=", + "unshifted_keycode": "Key_Equals", + "shifted_keycode": "Key_Plus", + "unshifted_char": "=", + "shifted_char": "+" + }, + { + "legend": "Backspace", + "keycode": "Key_Backspace", + "span": 1.5 + } + ], + [ + { + "legend": "Tab", + "keycode": "Key_Tab", + "span": 1.1 + }, + { + "legend": "Q", + "keycode": "Key_Q", + "unshifted_char": "q", + "shifted_char": "Q" + }, + { + "legend": "W", + "keycode": "Key_W", + "unshifted_char": "w", + "shifted_char": "W" + }, + { + "legend": "E", + "keycode": "Key_E", + "unshifted_char": "e", + "shifted_char": "E" + }, + { + "legend": "R", + "keycode": "Key_R", + "unshifted_char": "r", + "shifted_char": "R" + }, + { + "legend": "T", + "keycode": "Key_T", + "unshifted_char": "t", + "shifted_char": "T" + }, + { + "legend": "Y", + "keycode": "Key_Y", + "unshifted_char": "y", + "shifted_char": "Y" + }, + { + "legend": "U", + "keycode": "Key_U", + "unshifted_char": "u", + "shifted_char": "U" + }, + { + "legend": "I", + "keycode": "Key_I", + "unshifted_char": "i", + "shifted_char": "I" + }, + { + "legend": "O", + "keycode": "Key_O", + "unshifted_char": "o", + "shifted_char": "O" + }, + { + "legend": "P", + "keycode": "Key_P", + "unshifted_char": "p", + "shifted_char": "P" + }, + { + "legend": "{\n[", + "unshifted_keycode": "Key_BracketLeft", + "unshifted_keycode": "Key_BraceLeft", + "unshifted_char": "[", + "shifted_char": "{" + }, + { + "legend": "}\n]", + "unshifted_keycode": "Key_BracketRight", + "unshifted_keycode": "Key_BraceRight", + "unshifted_char": "]", + "shifted_char": "}" + }, + { + "legend": "|\n\\", + "unshifted_keycode": "Key_Backslash", + "unshifted_keycode": "Key_Bar", + "unshifted_char": "\\", + "shifted_char": "|", + "span": 1.3 + } + ], + [ + { + "legend": "Caps", + "keycode": "Key_CapsLock", + "span": 1.3 + }, + { + "legend": "A", + "keycode": "Key_A", + "unshifted_char": "a", + "shifted_char": "A" + }, + { + "legend": "S", + "keycode": "Key_S", + "unshifted_char": "s", + "shifted_char": "S" + }, + { + "legend": "D", + "keycode": "Key_D", + "unshifted_char": "d", + "shifted_char": "D" + }, + { + "legend": "F", + "keycode": "Key_F", + "unshifted_char": "f", + "shifted_char": "F" + }, + { + "legend": "G", + "keycode": "Key_G", + "unshifted_char": "g", + "shifted_char": "G" + }, + { + "legend": "H", + "keycode": "Key_H", + "unshifted_char": "h", + "shifted_char": "H" + }, + { + "legend": "J", + "keycode": "Key_J", + "unshifted_char": "j", + "shifted_char": "J" + }, + { + "legend": "K", + "keycode": "Key_K", + "unshifted_char": "k", + "shifted_char": "K" + }, + { + "legend": "L", + "keycode": "Key_L", + "unshifted_char": "l", + "shifted_char": "L" + }, + { + "legend": ":\n;", + "unshifted_keycode": "Key_Semicolon", + "shifted_keycode": "Key_Colon", + "unshifted_char": ";", + "shifted_char": ":" + }, + { + "legend": "\"\n'", + "unshifted_keycode": "Key_Apostrophe", + "shifted_keycode": "Key_QuoteDbl", + "unshifted_char": "'", + "shifted_char": "\"" + }, + { + "legend": "Return", + "keycode": "Key_Return", + "span": 1.5 + } + ], + [ + { + "legend": "Shift", + "keycode": "Key_Shift", + "span": 1.4 + }, + { + "legend": "Z", + "keycode": "Key_Z", + "unshifted_char": "z", + "shifted_char": "Z" + }, + { + "legend": "X", + "keycode": "Key_X", + "unshifted_char": "x", + "shifted_char": "X" + }, + { + "legend": "C", + "keycode": "Key_C", + "unshifted_char": "c", + "shifted_char": "C" + }, + { + "legend": "V", + "keycode": "Key_V", + "unshifted_char": "v", + "shifted_char": "V" + }, + { + "legend": "B", + "keycode": "Key_B", + "unshifted_char": "b", + "shifted_char": "B" + }, + { + "legend": "N", + "keycode": "Key_N", + "unshifted_char": "n", + "shifted_char": "N" + }, + { + "legend": "M", + "keycode": "Key_M", + "unshifted_char": "m", + "shifted_char": "M" + }, + { + "legend": "<\n,", + "unshifted_keycode": "Key_Comma", + "shifted_keycode": "Key_Less", + "unshifted_char": ",", + "shifted_char": "<" + }, + { + "legend": ">\n.", + "unshifted_keycode": "Key_Period", + "shifted_keycode": "Key_Greater", + "unshifted_char": ".", + "shifted_char": ">" + }, + { + "legend": "?\n/", + "unshifted_keycode": "Key_Slash", + "shifted_keycode": "Key_Question", + "unshifted_char": "/", + "shifted_char": "?" + }, + { + "legend": "Shift", + "keycode": "Key_Shift", + "span": 1.6 + } + ], + [ + { + "legend": "Ctrl", + "keycode": "Key_Control" + }, + { + "legend": "⌘", + "keycode": "Key_Meta" + }, + { + "legend": "Alt", + "keycode": "Key_Alt" + }, + { + "legend": " ", + "keycode": "Key_Space", + "unshifted_char": " ", + "shifted_char": " ", + "span": 4.0 + }, + { + "legend": "Alt", + "keycode": "Key_Alt" + }, + { + "legend": "⌘", + "keycode": "Key_Meta" + }, + { + "legend": "≡", + "keycode": "Key_Menu" + }, + { + "legend": "Ctrl", + "keycode": "Key_Control" + } + ] + ], + [ 0.003 ], + [ + 0.15, + [ + { + "legend": "🗗" + }, + { + "legend": "📋" + }, + {} + ], + { "verticalSpan": 0.003 }, + [ + { + "legend": "Ins", + "keycode": "Key_Insert" + }, + { + "legend": "Home", + "keycode": "Key_Home" + }, + { + "legend": "Page\nUp", + "keycode": "Key_PageUp" + } + ], + [ + { + "legend": "Del", + "keycode": "Key_Delete" + }, + { + "legend": "End", + "keycode": "Key_End" + }, + { + "legend": "Page\nDown", + "keycode": "Key_PageDown" + } + ], + {}, + [ + {}, + { + "legend": "↑", + "keycode": "Key_Up" + }, + {} + ], + [ + { + "legend": "â†", + "keycode": "Key_Left" + }, + { + "legend": "↓", + "keycode": "Key_Down" + }, + { + "legend": "→", + "keycode": "Key_Right" + } + ] + ] +] diff --git a/interface/resources/qml/overte/staging/keyboard/qmldir b/interface/resources/qml/overte/staging/keyboard/qmldir new file mode 100644 index 00000000000..0cd7e02edd0 --- /dev/null +++ b/interface/resources/qml/overte/staging/keyboard/qmldir @@ -0,0 +1,3 @@ +module Keyboard +Keyboard 1.0 Keyboard.qml +KeyboardKey 1.0 KeyboardKey.qml diff --git a/interface/resources/qml/overte/staging/login/LoginScreen.qml b/interface/resources/qml/overte/staging/login/LoginScreen.qml new file mode 100644 index 00000000000..550126e7528 --- /dev/null +++ b/interface/resources/qml/overte/staging/login/LoginScreen.qml @@ -0,0 +1,52 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Effects + +import "../" as Overte +import "./pages" as Pages + +Rectangle { + anchors.fill: parent + implicitWidth: 480 + implicitHeight: 720 + id: root + + color: Overte.Theme.paletteActive.base + + Image { + visible: !Overte.Theme.highContrast + anchors.fill: parent + source: "./assets/background.png" + + layer.enabled: true + layer.effect: MultiEffect { + blurEnabled: true + blurMax: 64 + blur: 1.0 + contrast: Overte.Theme.darkMode ? -0.7 : -0.8 + brightness: Overte.Theme.darkMode ? -0.2 : 0.25 + } + } + + ColumnLayout { + anchors.fill: parent + + Image { + Layout.fillWidth: true + + id: logo + fillMode: Image.PreserveAspectFit + + source: Overte.Theme.darkMode ? "./assets/logo_dark.png" : "./assets/logo_light.png" + } + + Overte.StackView { + Layout.fillWidth: true + Layout.fillHeight: true + + id: stack + initialItem: Pages.StartPage {} + } + } +} diff --git a/interface/resources/qml/overte/staging/login/assets/background.png b/interface/resources/qml/overte/staging/login/assets/background.png new file mode 100644 index 00000000000..9b18c52b57a Binary files /dev/null and b/interface/resources/qml/overte/staging/login/assets/background.png differ diff --git a/interface/resources/qml/overte/staging/login/assets/logo_dark.png b/interface/resources/qml/overte/staging/login/assets/logo_dark.png new file mode 100644 index 00000000000..bf3380a55d3 Binary files /dev/null and b/interface/resources/qml/overte/staging/login/assets/logo_dark.png differ diff --git a/interface/resources/qml/overte/staging/login/assets/logo_light.png b/interface/resources/qml/overte/staging/login/assets/logo_light.png new file mode 100644 index 00000000000..35285a0a0e3 Binary files /dev/null and b/interface/resources/qml/overte/staging/login/assets/logo_light.png differ diff --git a/interface/resources/qml/overte/staging/login/pages/LoginPage.qml b/interface/resources/qml/overte/staging/login/pages/LoginPage.qml new file mode 100644 index 00000000000..a22dc08c53a --- /dev/null +++ b/interface/resources/qml/overte/staging/login/pages/LoginPage.qml @@ -0,0 +1,137 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../../" as Overte + +Item { + // TODO: once the account server can accept + // usernames starting with numbers or underscores, modify this + readonly property var usernameRegex: /[a-zA-Z][0-9a-zA-Z_.-]{2,}/ + + // passwords must be at least 6 characters + readonly property var passwordRegex: /.{6,}/ + + ColumnLayout { + anchors.fill: parent + anchors.margins: 32 + spacing: Overte.Theme.fontPixelSize * 2 + + Overte.Label { + Layout.fillWidth: true + + text: qsTr("Log In") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Overte.TextField { + Layout.fillWidth: true + + id: usernameField + placeholderText: qsTr("Username") + + validator: RegularExpressionValidator { + regularExpression: usernameRegex + } + + Overte.ToolTip { + visible: parent.activeFocus && !parent.text.match(usernameRegex) + text: qsTr("Usernames must start with a letter and be at least 3 characters long.") + } + } + + RowLayout { + Layout.fillWidth: true + + Overte.TextField { + id: passwordField + Layout.fillWidth: true + placeholderText: qsTr("Password") + echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password + passwordMaskDelay: 500 + + validator: RegularExpressionValidator { + regularExpression: passwordRegex + } + + Overte.ToolTip { + visible: parent.activeFocus && !parent.text.match(usernameRegex) + text: qsTr("Passwords must be at least 6 characters.") + } + } + + Overte.RoundButton { + id: showPassword + checkable: true + icon.source: checked ? "../../icons/eye_closed.svg" : "../../icons/eye_open.svg" + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 24 + icon.height: 24 + } + } + + Overte.TextField { + Layout.fillWidth: true + + id: accountServerField + placeholderText: qsTr("Account server (Optional)") + } + + RowLayout { + spacing: 16 + Layout.maximumHeight: Overte.Theme.fontPixelSize * 3 + + Overte.Button { + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + text: qsTr("Back", "Return to previous page") + icon.source: "../../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + stack.pop(); + } + } + + Overte.Button { + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + text: qsTr("Log in", "Log in button") + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + enabled: usernameField.text.match(usernameRegex) && passwordField.text.match(passwordRegex) + + onClicked: { + let props = { + mode: "login", + username: usernameField.text, + // don't let stray newlines through + // or stuff can break + password: ( + passwordField.text + .replace("\n", "") + .replace("\r", "") + ), + }; + + if (accountServerField.text !== "") { + props.accountServer = accountServerField.text; + } + + stack.push("./ProgressPage.qml", props); + } + } + } + } +} diff --git a/interface/resources/qml/overte/staging/login/pages/ProgressPage.qml b/interface/resources/qml/overte/staging/login/pages/ProgressPage.qml new file mode 100644 index 00000000000..2dfd8e4e5fd --- /dev/null +++ b/interface/resources/qml/overte/staging/login/pages/ProgressPage.qml @@ -0,0 +1,83 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../../" as Overte + +Item { + required property string mode + required property string username + required property string password + property url accountServer: "https://mv.overte.org/server" + + function loginError(desc) { + let topLine = ( + mode === "register" ? + qsTr("Failed to register!") : + qsTr("Failed to log in!") + ); + statusText.text = `${topLine}\n\n${qsTr(desc)}`; + backButton.visible = true; + } + + // DEBUG + Timer { + interval: 1000 + running: true + onTriggered: { + loginError("Debug mode! Not connected to anything."); + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 32 + spacing: Overte.Theme.fontPixelSize * 2 + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + + id: statusText + text: mode === "register" ? qsTr("Registering…") : qsTr("Logging in…") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + spacing: 16 + Layout.minimumHeight: Overte.Theme.fontPixelSize * 3 + Layout.maximumHeight: Overte.Theme.fontPixelSize * 3 + + Overte.Button { + id: backButton + visible: false + + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + text: qsTr("Back", "Return to previous page") + icon.source: "../../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + stack.pop(); + } + } + + Item { + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } +} + diff --git a/interface/resources/qml/overte/staging/login/pages/RegisterPage.qml b/interface/resources/qml/overte/staging/login/pages/RegisterPage.qml new file mode 100644 index 00000000000..250aa3d9c08 --- /dev/null +++ b/interface/resources/qml/overte/staging/login/pages/RegisterPage.qml @@ -0,0 +1,137 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../../" as Overte + +Item { + // TODO: once the account server can accept + // usernames starting with numbers or underscores, modify this + readonly property var usernameRegex: /[a-zA-Z][0-9a-zA-Z_.-]{2,}/ + + // passwords must be at least 6 characters + readonly property var passwordRegex: /.{6,}/ + + ColumnLayout { + anchors.fill: parent + anchors.margins: 32 + spacing: Overte.Theme.fontPixelSize * 2 + + Overte.Label { + Layout.fillWidth: true + + text: qsTr("Register") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Overte.TextField { + Layout.fillWidth: true + + id: usernameField + placeholderText: qsTr("Username") + + validator: RegularExpressionValidator { + regularExpression: usernameRegex + } + + Overte.ToolTip { + visible: parent.activeFocus && !parent.text.match(usernameRegex) + text: qsTr("Usernames must start with a letter and be at least 3 characters long.") + } + } + + RowLayout { + Layout.fillWidth: true + + Overte.TextField { + id: passwordField + Layout.fillWidth: true + placeholderText: qsTr("Password") + echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password + passwordMaskDelay: 500 + + validator: RegularExpressionValidator { + regularExpression: passwordRegex + } + + Overte.ToolTip { + visible: parent.activeFocus && !parent.text.match(usernameRegex) + text: qsTr("Passwords must be at least 6 characters.") + } + } + + Overte.RoundButton { + id: showPassword + checkable: true + icon.source: checked ? "../../icons/eye_closed.svg" : "../../icons/eye_open.svg" + icon.color: Overte.Theme.paletteActive.buttonText + icon.width: 24 + icon.height: 24 + } + } + + Overte.TextField { + Layout.fillWidth: true + + id: accountServerField + placeholderText: qsTr("Account server (Optional)") + } + + RowLayout { + spacing: 16 + Layout.maximumHeight: Overte.Theme.fontPixelSize * 3 + + Overte.Button { + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + text: qsTr("Back", "Return to previous page") + icon.source: "../../icons/triangle_left.svg" + icon.width: 24 + icon.height: 24 + icon.color: Overte.Theme.paletteActive.buttonText + + onClicked: { + stack.pop(); + } + } + + Overte.Button { + // make the buttons equal size + Layout.preferredWidth: 1 + Layout.preferredHeight: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + text: qsTr("Register", "Register button") + backgroundColor: Overte.Theme.paletteActive.buttonAdd + + enabled: usernameField.text.match(usernameRegex) && passwordField.text.match(passwordRegex) + + onClicked: { + let props = { + mode: "register", + username: usernameField.text, + // don't let stray newlines through + // or stuff can break + password: ( + passwordField.text + .replace("\n", "") + .replace("\r", "") + ), + }; + + if (accountServerField.text !== "") { + props.accountServer = accountServerField.text; + } + + stack.push("./ProgressPage.qml", props); + } + } + } + } +} diff --git a/interface/resources/qml/overte/staging/login/pages/StartPage.qml b/interface/resources/qml/overte/staging/login/pages/StartPage.qml new file mode 100644 index 00000000000..9ab8c4b8624 --- /dev/null +++ b/interface/resources/qml/overte/staging/login/pages/StartPage.qml @@ -0,0 +1,69 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../../" as Overte + +Item { + readonly property url codeOfConduct: "https://overte.org/code_of_conduct.html" + + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 32 + spacing: Overte.Theme.fontPixelSize * 2 + + Overte.Label { + Layout.fillWidth: true + + text: qsTr("Welcome to Overte!\n\nAn account gives you a contacts list and lets you publish to the public worlds list.\n\nAccounts are entirely optional.") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } + + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + spacing: 16 + + Overte.Button { + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + + // force equally sized buttons + Layout.preferredWidth: 1 + + text: qsTr("Register") + + onClicked: { + stack.push("./RegisterPage.qml"); + } + } + + Overte.Button { + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + + // force equally sized buttons + Layout.preferredWidth: 1 + + text: qsTr("Log in") + + onClicked: { + stack.push("./LoginPage.qml"); + } + } + } + + Overte.Button { + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + + text: qsTr("Code of Conduct") + + onClicked: () => Qt.openUrlExternally(codeOfConduct) + } + } +} diff --git a/interface/resources/qml/overte/staging/login/pages/qmldir b/interface/resources/qml/overte/staging/login/pages/qmldir new file mode 100644 index 00000000000..816c3d70cec --- /dev/null +++ b/interface/resources/qml/overte/staging/login/pages/qmldir @@ -0,0 +1,4 @@ +module LoginPages +StartPage 1.0 StartPage.qml +LoginPage 1.0 LoginPage.qml +RegisterPage 1.0 RegisterPage.qml diff --git a/interface/resources/qml/overte/staging/login/qmldir b/interface/resources/qml/overte/staging/login/qmldir new file mode 100644 index 00000000000..02fe9379c9c --- /dev/null +++ b/interface/resources/qml/overte/staging/login/qmldir @@ -0,0 +1,2 @@ +module Login +LoginScreen 1.0 LoginScreen.qml diff --git a/interface/resources/qml/overte/staging/tutorial/AvatarPicker.qml b/interface/resources/qml/overte/staging/tutorial/AvatarPicker.qml new file mode 100644 index 00000000000..0702431e715 --- /dev/null +++ b/interface/resources/qml/overte/staging/tutorial/AvatarPicker.qml @@ -0,0 +1,6 @@ +import "../avatar_picker" + +AvatarPicker { + editable: false + availableTags: ["Human", "Furry", "Anime", "Robot", "Other"] +} diff --git a/interface/resources/qml/overte/staging/tutorial/ControlsGuide.qml b/interface/resources/qml/overte/staging/tutorial/ControlsGuide.qml new file mode 100644 index 00000000000..02d16d4fd5d --- /dev/null +++ b/interface/resources/qml/overte/staging/tutorial/ControlsGuide.qml @@ -0,0 +1,72 @@ +import QtQuick +import QtQuick.Layouts + +import "../" as Overte + +Rectangle { + id: root + implicitWidth: 720 + implicitHeight: 480 + color: Overte.Theme.paletteActive.base + + Overte.TabBar { + id: tabBar + anchors.top: root.top + anchors.left: root.left + anchors.right: root.right + + Overte.TabButton { width: implicitWidth; text: qsTr("Keyboard") } + Overte.TabButton { width: implicitWidth; text: qsTr("Oculus Touch") } + Overte.TabButton { width: implicitWidth; text: qsTr("Mixed Reality") } + Overte.TabButton { width: implicitWidth; text: qsTr("Index") } + Overte.TabButton { width: implicitWidth; text: qsTr("Vive") } + } + + StackLayout { + anchors.top: tabBar.bottom + anchors.left: root.left + anchors.right: root.right + anchors.bottom: root.bottom + currentIndex: tabBar.currentIndex + + // Keyboard + Image { + Layout.fillWidth: true + Layout.fillHeight: true + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/controls_dark.png" : "./assets/controls_light.png" + } + + // Oculus Touch + Image { + Layout.fillWidth: true + Layout.fillHeight: true + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/controls_dark.png" : "./assets/controls_light.png" + } + + // Mixed Reality + Image { + Layout.fillWidth: true + Layout.fillHeight: true + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/controls_dark.png" : "./assets/controls_light.png" + } + + // Index + Image { + Layout.fillWidth: true + Layout.fillHeight: true + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/controls_dark.png" : "./assets/controls_light.png" + } + + // Vive + Image { + Layout.fillWidth: true + Layout.fillHeight: true + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/controls_dark.png" : "./assets/controls_light.png" + } + } +} diff --git a/interface/resources/qml/overte/staging/tutorial/Welcome.qml b/interface/resources/qml/overte/staging/tutorial/Welcome.qml new file mode 100644 index 00000000000..610cd8e14b0 --- /dev/null +++ b/interface/resources/qml/overte/staging/tutorial/Welcome.qml @@ -0,0 +1,46 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import "../" as Overte + +Rectangle { + id: root + implicitWidth: 480 + implicitHeight: 360 + color: Overte.Theme.paletteActive.base + + ScrollView { + anchors.fill: parent + + ScrollBar.vertical: Overte.ScrollBar { + policy: ScrollBar.AsNeeded + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + } + + contentWidth: width - ScrollBar.vertical.width + + Column { + spacing: 16 + padding: 8 + + Image { + width: root.width + height: sourceSize.height + fillMode: Image.PreserveAspectFit + source: Overte.Theme.darkMode ? "./assets/logo_dark.png" : "./assets/logo_light.png" + } + + Overte.Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: root.width / 8 + anchors.rightMargin: root.width / 8 + text: qsTr("Welcome to Overte!\nThis is an offline tutorial world.\n\nTODO: More intro text") + wrapMode: Text.Wrap + } + } + } +} diff --git a/interface/resources/qml/overte/staging/tutorial/assets/controls_dark.png b/interface/resources/qml/overte/staging/tutorial/assets/controls_dark.png new file mode 100644 index 00000000000..3914ae96af6 Binary files /dev/null and b/interface/resources/qml/overte/staging/tutorial/assets/controls_dark.png differ diff --git a/interface/resources/qml/overte/staging/tutorial/assets/controls_light.png b/interface/resources/qml/overte/staging/tutorial/assets/controls_light.png new file mode 100644 index 00000000000..52abbe450d8 Binary files /dev/null and b/interface/resources/qml/overte/staging/tutorial/assets/controls_light.png differ diff --git a/interface/resources/qml/overte/staging/tutorial/assets/logo_dark.png b/interface/resources/qml/overte/staging/tutorial/assets/logo_dark.png new file mode 100644 index 00000000000..bf3380a55d3 Binary files /dev/null and b/interface/resources/qml/overte/staging/tutorial/assets/logo_dark.png differ diff --git a/interface/resources/qml/overte/staging/tutorial/assets/logo_light.png b/interface/resources/qml/overte/staging/tutorial/assets/logo_light.png new file mode 100644 index 00000000000..35285a0a0e3 Binary files /dev/null and b/interface/resources/qml/overte/staging/tutorial/assets/logo_light.png differ diff --git a/interface/resources/qml/overte/staging/tutorial/qmldir b/interface/resources/qml/overte/staging/tutorial/qmldir new file mode 100644 index 00000000000..ed867fa3f36 --- /dev/null +++ b/interface/resources/qml/overte/staging/tutorial/qmldir @@ -0,0 +1,4 @@ +module Tutorial +Welcome 1.0 Welcome.qml +ControlsGuide 1.0 ControlsGuide.qml +AvatarPicker 1.0 AvatarPicker.qml diff --git a/interface/resources/qml/windows/Decoration.qml b/interface/resources/qml/windows/Decoration.qml index 1ba7ad125d6..1d2d6320ec8 100644 --- a/interface/resources/qml/windows/Decoration.qml +++ b/interface/resources/qml/windows/Decoration.qml @@ -9,22 +9,24 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 import "." import stylesUit 1.0 +// TODO: look into whether we should outright replace the Hifi desktop system +import "../overte" as Overte + Rectangle { HifiConstants { id: hifi } signal inflateDecorations(); signal deflateDecorations(); - property int frameMargin: 9 + property int frameMargin: 4 property int frameMarginLeft: frameMargin property int frameMarginRight: frameMargin - property int frameMarginTop: 2 * frameMargin + iconSize - property int frameMarginBottom: iconSize + 11 + property int frameMarginTop: (2 * frameMargin) + iconSize + property int frameMarginBottom: (2 * frameMargin) + (window.resizable || DebugQML ? 18 : 0) anchors { topMargin: -frameMarginTop @@ -33,12 +35,18 @@ Rectangle { bottomMargin: -frameMarginBottom } anchors.fill: parent - color: hifi.colors.baseGrayHighlight40 - border { - width: hifi.dimensions.borderWidth - color: hifi.colors.faintGray50 + color: Overte.Theme.paletteActive.base + border.width: Overte.Theme.borderWidth + border.color: { + if (Overte.Theme.highContrast) { + return Overte.Theme.paletteActive.text; + } else if (Overte.Theme.darkMode) { + return Qt.lighter(Overte.Theme.paletteActive.base); + } else { + return Qt.darker(Overte.Theme.paletteActive.base); + } } - radius: hifi.dimensions.borderRadius + radius: Overte.Theme.borderRadius // Enable dragging of the window, // detect mouseover of the window (including decoration) @@ -90,4 +98,3 @@ Rectangle { } } } - diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml index fb0dd55985c..67a2033e494 100644 --- a/interface/resources/qml/windows/DefaultFrameDecoration.qml +++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml @@ -8,52 +8,74 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import QtQuick +import QtQuick.Layouts import "." import stylesUit 1.0 +// TODO: look into whether we should outright replace the Hifi desktop system +import "../overte" as Overte + Decoration { HifiConstants { id: hifi } // Dialog frame id: root - property int iconSize: hifi.dimensions.frameIconSize - frameMargin: 9 + property int iconSize: 24 + frameMargin: 4 frameMarginLeft: frameMargin frameMarginRight: frameMargin - frameMarginTop: 2 * frameMargin + iconSize - frameMarginBottom: iconSize + 11 + frameMarginTop: (2 * frameMargin) + iconSize + frameMarginBottom: (2 * frameMargin) + (window.resizable || DebugQML ? 18 : 0) onInflateDecorations: { if (!HMD.active) { return; } - root.frameMargin = 18 - titleText.size = hifi.fontSizes.overlayTitle * 2 - root.iconSize = hifi.dimensions.frameIconSize * 2 + root.frameMargin = 18; + root.iconSize = 32; } onDeflateDecorations: { - root.frameMargin = 9 - titleText.size = hifi.fontSizes.overlayTitle - root.iconSize = hifi.dimensions.frameIconSize + root.frameMargin = 4; + root.iconSize = 24; } + Rectangle { + anchors.fill: controlsRow + color: Overte.Theme.paletteActive.activeWindowTitleBg + topLeftRadius: Overte.Theme.borderRadius + topRightRadius: Overte.Theme.borderRadius + } - Row { + RowLayout { id: controlsRow anchors { - right: parent.right; - top: parent.top; - topMargin: root.frameMargin + 1 // Move down a little to visually align with the title - rightMargin: root.frameMarginRight; + top: parent.top + left: parent.left + right: parent.right + margins: Overte.Theme.borderWidth + } + spacing: 2 + height: root.frameMarginTop - root.frameMargin + + Overte.Label { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: 4 + + // Title + id: titleText + text: window ? window.title : "" + verticalAlignment: Text.AlignVCenter + color: Overte.Theme.paletteActive.activeWindowTitleFg } - spacing: root.iconSize / 4 HiFiGlyphs { + Layout.alignment: Qt.AlignCenter + // "Pin" button visible: window.pinnable text: window.pinned ? hifi.glyphs.pinInverted : hifi.glyphs.pin @@ -68,49 +90,28 @@ Decoration { } } - HiFiGlyphs { - // "Close" button + Overte.RoundButton { + Layout.alignment: Qt.AlignCenter + Layout.rightMargin: 4 + visible: window ? window.closable : false - text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close - color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white - size: root.iconSize - MouseArea { - id: closeClickArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - window.shown = false; - window.windowClosed(); - } - } - } - } + icon.source: "../overte/icons/close.svg" + icon.width: root.iconSize == 24 ? 12 : 24 + icon.height: root.iconSize == 24 ? 12 : 24 + icon.color: Overte.Theme.paletteActive.buttonText + backgroundColor: ( + hovered ? + Overte.Theme.paletteActive.buttonDestructive : + Overte.Theme.paletteActive.button + ) - RalewayRegular { - // Title - id: titleText - anchors { - left: parent.left - leftMargin: root.frameMarginLeft + hifi.dimensions.contentMargin.x - right: controlsRow.left - rightMargin: root.iconSize - top: parent.top - topMargin: root.frameMargin - } - text: window ? window.title : "" - color: hifi.colors.white - size: hifi.fontSizes.overlayTitle - } + implicitWidth: root.iconSize + implicitHeight: root.iconSize - DropShadow { - source: titleText - anchors.fill: titleText - horizontalOffset: 2 - verticalOffset: 2 - samples: 2 - color: hifi.colors.baseGrayShadow60 - visible: (desktop.gradientsSupported && window && window.focus) - cached: true + onClicked: { + window.shown = false; + window.windowClosed(); + } + } } } - diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml index cfff40bbf62..58c31540166 100644 --- a/interface/resources/qml/windows/Frame.qml +++ b/interface/resources/qml/windows/Frame.qml @@ -9,11 +9,14 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 import "../js/Utils.js" as Utils +// TODO: look into whether we should outright replace the Hifi desktop system +import "../overte" as Overte + Item { id: frame objectName: "Frame" @@ -21,7 +24,6 @@ Item { default property var decoration property string qmlFile: "N/A" - property bool gradientsSupported: desktop.gradientsSupported readonly property int frameMarginLeft: frame.decoration ? frame.decoration.frameMarginLeft : 0 readonly property int frameMarginRight: frame.decoration ? frame.decoration.frameMarginRight : 0 @@ -33,21 +35,12 @@ Item { anchors.fill: parent children: [ - focusShadow, decoration, sizeOutline, debugZ, sizeDrag ] - Text { - id: debugZ - visible: (typeof DebugQML !== 'undefined') ? DebugQML : false - color: "red" - text: (window ? "Z: " + window.z : "") + " " + qmlFile - y: window ? window.height + 4 : 0 - } - function deltaSize(dx, dy) { var newSize = Qt.vector2d(window.width + dx, window.height + dy); newSize = Utils.clampVector(newSize, window.minSize, window.maxSize); @@ -55,41 +48,32 @@ Item { window.height = newSize.y } - RadialGradient { - id: focusShadow - width: 1.66 * window.width - height: 1.66 * window.height - x: (window.width - width) / 2 - y: window.height / 2 - 0.375 * height - visible: gradientsSupported && window && window.focus && window.content.visible - gradient: Gradient { - // GradientStop position 0.5 is at full circumference of circle that fits inside the square. - GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity - GradientStop { position: 0.333; color: "#1f000000" } // black, 12% opacity - GradientStop { position: 0.5; color: "#00000000" } // black, 0% opacity - GradientStop { position: 1.0; color: "#00000000" } - } - cached: true - } - Rectangle { id: sizeOutline x: -frameMarginLeft y: -frameMarginTop width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0 height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0 - color: hifi.colors.baseGrayHighlight15 - border.width: 3 - border.color: hifi.colors.white50 - radius: hifi.dimensions.borderRadius + color: Overte.Theme.paletteActive.base + border.width: Overte.Theme.borderWidth + border.color: { + if (Overte.Theme.highContrast) { + return Overte.Theme.paletteActive.text; + } else if (Overte.Theme.darkMode) { + return Qt.lighter(Overte.Theme.paletteActive.base); + } else { + return Qt.darker(Overte.Theme.paletteActive.base); + } + } + radius: Overte.Theme.borderRadius visible: window ? !window.content.visible : false } MouseArea { // Resize handle id: sizeDrag - width: hifi.dimensions.frameIconSize - height: hifi.dimensions.frameIconSize + width: 18 + height: 18 enabled: window ? window.resizable : false hoverEnabled: true x: window ? window.width + frameMarginRight - hifi.dimensions.frameIconSize : 0 @@ -119,15 +103,30 @@ Item { frame.deltaSize(delta.x, delta.y) } } + + // TODO: use an SVG icon instead of HifiGlyphs HiFiGlyphs { + x: -9 + y: -9 visible: sizeDrag.enabled - x: -11 // Move a little to visually align - y: window.modality == Qt.ApplicationModal ? -6 : -4 text: hifi.glyphs.resizeHandle - size: hifi.dimensions.frameIconSize + 10 - color: sizeDrag.containsMouse || sizeDrag.pressed - ? hifi.colors.white - : (window.colorScheme == hifi.colorSchemes.dark ? hifi.colors.white50 : hifi.colors.lightGrayText80) + size: 32 + color: { + if (Overte.Theme.highContrast) { + return Overte.Theme.paletteActive.buttonText; + } else { + return Qt.darker(Overte.Theme.paletteActive.base, Overte.Theme.borderDarker); + } + } } } + + Text { + id: debugZ + visible: (typeof DebugQML !== 'undefined') ? DebugQML : false + color: "red" + text: (window ? "Z: " + window.z : "") + " " + qmlFile + y: window ? window.height + 4 : 0 + font.pixelSize: 12 + } } diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index 0c91b58e315..51a62c5d075 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -11,12 +11,14 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." as Windows import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit +import "../overte" as Overte + // FIXME how do I set the initial position of a window without // overriding places where the a individual client of the window // might be setting the position with a Settings{} element? @@ -55,25 +57,10 @@ Windows.Window { id: contentBackground anchors.fill: parent //anchors.rightMargin: parent.isScrolling ? verticalScrollWidth + 1 : 0 - color: hifi.colors.baseGray + color: Overte.Theme.paletteActive.base visible: !window.hideBackground && modality != Qt.ApplicationModal } - LinearGradient { - visible: !window.hideBackground && gradientsSupported && modality != Qt.ApplicationModal - anchors.top: contentBackground.bottom - anchors.left: contentBackground.left - width: contentBackground.width - 1 - height: 4 - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.darkGray } - GradientStop { position: 1.0; color: hifi.colors.darkGray0 } - } - cached: true - } - Flickable { id: scrollView contentItem.children: [ content ] @@ -176,6 +163,7 @@ Windows.Window { children: [ footer ] } + // TODO: remove the old 2D hifi keyboard HifiControlsUit.Keyboard { id: keyboard enabled: !keyboardOverride diff --git a/interface/resources/qml/windows/ToolFrame.qml b/interface/resources/qml/windows/ToolFrame.qml index bb2bada498e..8ea5bd669ba 100644 --- a/interface/resources/qml/windows/ToolFrame.qml +++ b/interface/resources/qml/windows/ToolFrame.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." import stylesUit 1.0 diff --git a/interface/resources/qml/windows/ToolFrameDecoration.qml b/interface/resources/qml/windows/ToolFrameDecoration.qml index 4f149037b38..2e7044123e9 100644 --- a/interface/resources/qml/windows/ToolFrameDecoration.qml +++ b/interface/resources/qml/windows/ToolFrameDecoration.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." import stylesUit 1.0 diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index 77cb35ceba4..64b26f28ab7 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import "." import stylesUit 1.0 @@ -67,7 +67,9 @@ Fadable { property bool destroyOnCloseButton: true // Should hiding the window destroy it or just hide it? property bool destroyOnHidden: false - property bool pinnable: true + // TODO: Remove the pinnable property entirely + // Nobody knows what the pin is supposed to do, disable it by default + property bool pinnable: false property bool pinned: false property bool resizable: false property bool gradientsSupported: desktop.gradientsSupported @@ -127,7 +129,7 @@ Fadable { propagateComposedEvents: true acceptedButtons: Qt.AllButtons enabled: window.visible - onPressed: { + onPressed: mouse => { window.raise(); mouse.accepted = false; } @@ -142,7 +144,7 @@ Fadable { propagateComposedEvents: true acceptedButtons: Qt.AllButtons enabled: window.visible - onPressed: { + onPressed: mouse => { frame.forceActiveFocus(); mouse.accepted = false; } @@ -291,7 +293,7 @@ Fadable { window.height - frame.decoration.anchors.topMargin - frame.decoration.anchors.bottomMargin) } - Keys.onPressed: { + Keys.onPressed: event => { switch(event.key) { case Qt.Key_Control: case Qt.Key_Shift: diff --git a/interface/resources/serverless/Scripts/Wizard.qml b/interface/resources/serverless/Scripts/Wizard.qml index f258de9247b..2799bcc5df1 100644 --- a/interface/resources/serverless/Scripts/Wizard.qml +++ b/interface/resources/serverless/Scripts/Wizard.qml @@ -49,7 +49,7 @@ Rectangle { loader.sourceComponent = step5; break; default: - loader.setSource(undefined); + loader.setSource(""); } } diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index e5007d706e8..f242273479a 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -23,7 +23,7 @@ #define qApp (static_cast(QCoreApplication::instance())) AndroidHelper::AndroidHelper() { - qRegisterMetaType("QAudio::Mode"); + qRegisterMetaType("QAudioDevice::Mode"); } AndroidHelper::~AndroidHelper() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0c656d209a9..a50506379f9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -14,7 +14,6 @@ #include "Application.h" -#include #include #include #include @@ -216,7 +215,9 @@ Application::Application( ) : QApplication(argc, argv), #ifdef USE_GL - _window(new MainWindow(desktop())), + // QT6TODO: is this correct? + _window(new MainWindow()), + //_window(new MainWindow(desktop())), #else _vkWindow(new VKWindow()), _vkWindowWrapper(QWidget::createWindowContainer(_vkWindow)), @@ -235,6 +236,15 @@ Application::Application( _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _previousPreferredDisplayMode("previousPreferredDisplayMode", 0), // UI + _virtualPointingDevice(std::make_shared( + "OffscreenUiPointingDevice", + 0, + QInputDevice::DeviceType::AllDevices, + QPointingDevice::PointerType::AllPointerTypes, + QInputDevice::Capability::All, + 4, // maxPoints + 2 // buttonCount + )), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), _desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT), _desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR), @@ -245,8 +255,8 @@ Application::Application( _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), - _darkTheme("darkTheme", true), _miniTabletEnabledSetting("miniTabletEnabled", DEFAULT_MINI_TABLET_ENABLED), + _themePrefs(new ThemePrefs(this)), // Entities _maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS), _maxOctreePPS(_maxOctreePacketsPerSecond.get()), @@ -415,7 +425,7 @@ QString Application::getUserAgent() { if (QThread::currentThread() != thread()) { QString userAgent; - BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_RETURN_ARG(QString, userAgent)); + BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_GENERIC_RETURN_ARG(QString, userAgent)); return userAgent; } @@ -613,7 +623,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint scriptEngine->registerGlobalObject(sgp, "Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject(sgp, "DesktopPreviewProvider", DependencyManager::get().data()); #if !defined(DISABLE_QML) - scriptEngine->registerGlobalObject(sgp, "Stats", Stats::getInstance()); + // QT6TODO: Stats instance is not created? + //scriptEngine->registerGlobalObject(sgp, "Stats", Stats::getInstance()); #endif scriptEngine->registerGlobalObject(sgp, "Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject(sgp, "Snapshot", DependencyManager::get().data()); @@ -1009,7 +1020,7 @@ std::map Application::prepareServerlessDomainContents(QUrl dom void Application::loadServerlessDomain(QUrl domainURL) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL)); + QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_GENERIC_ARG(QUrl, domainURL)); return; } @@ -1041,7 +1052,7 @@ void Application::loadServerlessDomain(QUrl domainURL) { void Application::loadErrorDomain(QUrl domainURL) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadErrorDomain", Q_ARG(QUrl, domainURL)); + QMetaObject::invokeMethod(this, "loadErrorDomain", Q_GENERIC_ARG(QUrl, domainURL)); return; } @@ -1289,7 +1300,13 @@ void Application::loadSettings(const QCommandLineParser& parser) { externalResource->setBase(bucket, url.get()); } - // the setter function isn't called, so update the theme colors now + connect(_themePrefs, &ThemePrefs::themeChanged, [this]() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(qApp, "updateThemeColors"); + } else { + updateThemeColors(); + } + }); updateThemeColors(); _settingsLoaded = true; diff --git a/interface/src/Application.h b/interface/src/Application.h index 327eb6b900d..1de2d0ee462 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -44,6 +44,7 @@ #include "ui/overlays/Overlays.h" #include "VisionSqueeze.h" #include "workload/GameWorkload.h" +#include "ThemePrefs.h" #ifndef USE_GL #include "VKCanvas.h" #endif @@ -93,6 +94,8 @@ class Application : public QApplication, ); ~Application(); + bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted); + /** * @brief Initialize everything * @@ -177,6 +180,8 @@ class Application : public QApplication, const ApplicationOverlay& getApplicationOverlay() const { return *_applicationOverlay; } CompositorHelper& getApplicationCompositor() const; + std::shared_ptr getVirtualPointingDevice() const; + virtual PickRay computePickRay(float x, float y) const override; static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); @@ -325,7 +330,8 @@ class Application : public QApplication, int getMaxOctreePacketsPerSecond() const { return _maxOctreePPS; } bool isMissingSequenceNumbers() { return _isMissingSequenceNumbers; } - NodeToOctreeSceneStats* getOcteeSceneStats() { return _octreeProcessor->getOctreeSceneStats(); } + // This function returns a value only when octree processor is available. + std::optional getOcteeSceneStats(); // Assets @@ -392,7 +398,6 @@ class Application : public QApplication, void miniTabletEnabledChanged(bool enabled); void awayStateWhenFocusLostInVRChanged(bool enabled); - void darkThemePreferenceChanged(bool useDarkTheme); void menuBarVisibilityChanged(bool visible); public slots: @@ -469,8 +474,7 @@ public slots: const QString getPreferredCursor() const { return _preferredCursor.get(); } void setPreferredCursor(const QString& cursor); - bool getDarkThemePreference() const { return _darkTheme.get(); } - void setDarkThemePreference(bool value); + ThemePrefs* themePrefs() { return _themePrefs; } bool getMenuBarVisible() const { return _menuBarVisible.get(); } void setMenuBarVisible(bool visible); @@ -789,6 +793,8 @@ private slots: bool _cursorNeedsChanging { false }; bool _useSystemCursor { false }; + std::shared_ptr _virtualPointingDevice; + DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface; QPointer _logDialog; @@ -826,13 +832,12 @@ private slots: Setting::Handle _constrainToolbarPosition; Setting::Handle _awayStateWhenFocusLostInVREnabled; Setting::Handle _preferredCursor; - // TODO Qt6: Qt5 doesn't have anything for system theme preferences, Qt6.5+ does - Setting::Handle _darkTheme; Setting::Handle _miniTabletEnabledSetting; Setting::Handle _keepLogWindowOnTop { "keepLogWindowOnTop", false }; Setting::Handle _menuBarVisible { "menuBarVisible", true }; - void updateThemeColors(); + ThemePrefs *_themePrefs; + Q_INVOKABLE void updateThemeColors(); // Plugins @@ -853,7 +858,13 @@ private slots: // Events - QHash _keysPressed; + class KeyEventRecord { + public: + KeyEventRecord(const int key, const QString &text) : key(key), text(text) {} + int key; + QString text; + }; + QHash _keysPressed; TouchEvent _lastTouchEvent; quint64 _lastAcceptedKeyPress { 0 }; diff --git a/interface/src/ApplicationEventHandler.h b/interface/src/ApplicationEventHandler.h index d7e5bf171c7..7a252d3b171 100644 --- a/interface/src/ApplicationEventHandler.h +++ b/interface/src/ApplicationEventHandler.h @@ -24,10 +24,12 @@ #include "Application.h" #ifdef Q_OS_WIN -static const UINT UWM_IDENTIFY_INSTANCES = - RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); -static const UINT UWM_SHOW_APPLICATION = - RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME")); +#include + +static const UINT UWM_IDENTIFY_INSTANCES = RegisterWindowMessage( + qUtf16Printable("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qEnvironmentVariable("USERNAME"))); +static const UINT UWM_SHOW_APPLICATION = RegisterWindowMessage( + qUtf16Printable("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qEnvironmentVariable("USERNAME"))); class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -36,7 +38,7 @@ class MyNativeEventFilter : public QAbstractNativeEventFilter { return staticInstance; } - bool nativeEventFilter(const QByteArray &eventType, void* msg, long* result) Q_DECL_OVERRIDE { + bool nativeEventFilter(const QByteArray& eventType, void* msg, qintptr* result) Q_DECL_OVERRIDE { if (eventType == "windows_generic_MSG") { MSG* message = (MSG*)msg; diff --git a/interface/src/Application_Assets.cpp b/interface/src/Application_Assets.cpp index 96404063b67..4ac698786e0 100644 --- a/interface/src/Application_Assets.cpp +++ b/interface/src/Application_Assets.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -171,7 +172,7 @@ void Application::addAssetToWorld(QString path, QString zipFile, bool isZip) { QString mapping; QString filename = filenameFromPath(path); if (isZip) { - QString assetName = zipFile.section("/", -1).remove(QRegExp("[.]zip(.*)$")); + QString assetName = zipFile.section("/", -1).remove(QRegularExpression("[.]zip(.*)$")); QString assetFolder = path.section("model_repo/", -1); mapping = "/" + assetName + "/" + assetFolder; } else { diff --git a/interface/src/Application_Entities.cpp b/interface/src/Application_Entities.cpp index 1ef78e84b60..1c1d7673e32 100644 --- a/interface/src/Application_Entities.cpp +++ b/interface/src/Application_Entities.cpp @@ -37,6 +37,15 @@ void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { } } +std::optional Application::getOcteeSceneStats() { + // On start this function is sometimes called before _octreeProcessor is available. + if (_octreeProcessor) { + return _octreeProcessor->getOctreeSceneStats(); + } else { + return {}; + } +} + QVector Application::pasteEntities(const QString& entityHostType, float x, float y, float z) { return _entityClipboard->sendEntities(_entityEditSender.get(), getEntities()->getTree(), entityHostType, x, y, z); } @@ -313,9 +322,11 @@ void Application::clearDomainOctreeDetails(bool clearAll) { setIsInterstitialMode(true); auto octreeServerSceneStats = getOcteeSceneStats(); - octreeServerSceneStats->withWriteLock([&] { - octreeServerSceneStats->clear(); - }); + if (octreeServerSceneStats) { + octreeServerSceneStats.value()->withWriteLock([&] { + octreeServerSceneStats.value()->clear(); + }); + } // reset the model renderer clearAll ? getEntities()->clear() : getEntities()->clearDomainAndNonOwnedEntities(); @@ -413,16 +424,18 @@ int Application::sendNackPackets() { QSet missingSequenceNumbers; auto octreeServerSceneStats = getOcteeSceneStats(); - octreeServerSceneStats->withReadLock([&] { - // retrieve octree scene stats of this node - if (octreeServerSceneStats->find(nodeUUID) == octreeServerSceneStats->end()) { - return; - } - // get sequence number stats of node, prune its missing set, and make a copy of the missing set - SequenceNumberStats& sequenceNumberStats = (*octreeServerSceneStats)[nodeUUID].getIncomingOctreeSequenceNumberStats(); - sequenceNumberStats.pruneMissingSet(); - missingSequenceNumbers = sequenceNumberStats.getMissingSet(); - }); + if (octreeServerSceneStats) { + octreeServerSceneStats.value()->withReadLock([&] { + // retrieve octree scene stats of this node + if (octreeServerSceneStats.value()->find(nodeUUID) == octreeServerSceneStats.value()->end()) { + return; + } + // get sequence number stats of node, prune its missing set, and make a copy of the missing set + SequenceNumberStats& sequenceNumberStats = (*octreeServerSceneStats.value())[nodeUUID].getIncomingOctreeSequenceNumberStats(); + sequenceNumberStats.pruneMissingSet(); + missingSequenceNumbers = sequenceNumberStats.getMissingSet(); + }); + } _isMissingSequenceNumbers = (missingSequenceNumbers.size() != 0); diff --git a/interface/src/Application_Events.cpp b/interface/src/Application_Events.cpp index cd3a9327e48..d29c92c9338 100644 --- a/interface/src/Application_Events.cpp +++ b/interface/src/Application_Events.cpp @@ -55,6 +55,10 @@ class LambdaEvent : public QEvent { void call() const { _fun(); } }; +std::shared_ptr Application::getVirtualPointingDevice() const { + return _virtualPointingDevice; +} + bool Application::notify(QObject* object, QEvent* event) { if (thread() == QThread::currentThread()) { PROFILE_RANGE_IF_LONGER(app, "notify", 2) @@ -211,7 +215,8 @@ bool Application::eventFilter(QObject* object, QEvent* event) { QMouseEvent* newEvent = new QMouseEvent( QEvent::MouseButtonPress, mouseEvent->localPos(), mouseEvent->windowPos(), mouseEvent->screenPos(), Qt::RightButton, Qt::MouseButtons(Qt::RightButton), - mouseEvent->modifiers()); + mouseEvent->modifiers(), + getVirtualPointingDevice().get()); QApplication::postEvent(object, newEvent); return true; } @@ -336,7 +341,7 @@ void Application::windowMinimizedChanged(bool minimized) { void Application::keyPressEvent(QKeyEvent* event) { if (!event->isAutoRepeat()) { - _keysPressed.insert(event->key(), *event); + _keysPressed.insert(event->key(), KeyEventRecord(event->key(), event->text())); } _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts @@ -607,10 +612,10 @@ void Application::synthesizeKeyReleasEvents() { // synthesize events for keys currently pressed, since we may not get their release events // Because our key event handlers may manipulate _keysPressed, lets swap the keys pressed into a local copy, // clearing the existing list. - QHash keysPressed; + QHash keysPressed; std::swap(keysPressed, _keysPressed); - for (auto& ev : keysPressed) { - QKeyEvent synthesizedEvent { QKeyEvent::KeyRelease, ev.key(), Qt::NoModifier, ev.text() }; + for (auto& eventRecord : keysPressed) { + QKeyEvent synthesizedEvent { QKeyEvent::KeyRelease, eventRecord.key, Qt::NoModifier, eventRecord.text }; keyReleaseEvent(&synthesizedEvent); } } @@ -652,11 +657,13 @@ void Application::mouseMoveEvent(QMouseEvent* event) { QMouseEvent mappedEvent(event->type(), transformedPos, - event->screenPos(), button, - buttons, event->modifiers()); + event->globalPosition(), button, + buttons, event->modifiers(), + getVirtualPointingDevice().get()); if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) { + // QT6TODO: compositor.getReticleOverDesktop() does not work currently. getEntities()->mouseMoveEvent(&mappedEvent); } @@ -687,7 +694,12 @@ void Application::mousePressEvent(QMouseEvent* event) { QPointF transformedPos; #endif - QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); + QMouseEvent mappedEvent(event->type(), + transformedPos, + event->globalPosition(), event->button(), + event->buttons(), event->modifiers(), + getVirtualPointingDevice().get()); + QUuid result = getEntities()->mousePressEvent(&mappedEvent); setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID); @@ -726,8 +738,9 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { #endif QMouseEvent mappedEvent(event->type(), transformedPos, - event->screenPos(), event->button(), - event->buttons(), event->modifiers()); + event->globalPosition(), event->button(), + event->buttons(), event->modifiers(), + getVirtualPointingDevice().get()); getEntities()->mouseDoublePressEvent(&mappedEvent); // if one of our scripts have asked to capture this event, then stop processing it @@ -748,8 +761,9 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { #endif QMouseEvent mappedEvent(event->type(), transformedPos, - event->screenPos(), event->button(), - event->buttons(), event->modifiers()); + event->globalPosition(), event->button(), + event->buttons(), event->modifiers(), + getVirtualPointingDevice().get()); getEntities()->mouseReleaseEvent(&mappedEvent); diff --git a/interface/src/Application_Graphics.cpp b/interface/src/Application_Graphics.cpp index 50a325370ee..9ec171e7595 100644 --- a/interface/src/Application_Graphics.cpp +++ b/interface/src/Application_Graphics.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -57,6 +58,8 @@ #include "Menu.h" #include "webbrowser/WebBrowserSuggestionsEngine.h" +#include + #if defined(Q_OS_ANDROID) #include "AndroidHelper.h" #endif @@ -81,6 +84,7 @@ void Application::initializeGL() { } #ifdef USE_GL + _primaryWidget->windowHandle()->setSurfaceType(QSurface::OpenGLSurface); _primaryWidget->windowHandle()->setFormat(getDefaultOpenGLSurfaceFormat()); #else //_primaryWidget->windowHandle()->setFormat(getDefaultOpenGLSurfaceFormat()); // VKTODO @@ -163,6 +167,7 @@ void Application::initializeGL() { { OffscreenGLCanvas* qmlShareContext = new OffscreenGLCanvas(); qmlShareContext->setObjectName("QmlShareContext"); + qmlShareContext->getContext()->setFormat(getDefaultOpenGLSurfaceFormat()); qmlShareContext->create(globalShareContext); if (!qmlShareContext->makeCurrent()) { qCWarning(interfaceapp, "Unable to make QML shared context current"); @@ -222,7 +227,7 @@ static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, in } auto parent = menu->getMenu(MenuOption::OutputMenu); auto action = menu->addActionToQMenuAndActionHash(parent, - name, QKeySequence(Qt::CTRL + (Qt::Key_0 + index)), qApp, + name, QKeySequence(Qt::CTRL | static_cast(static_cast(Qt::Key_0) + index)), qApp, SLOT(updateDisplayMode()), QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu); @@ -243,12 +248,12 @@ void Application::initializeUi() { auto newValidator = [=](const QUrl& url) -> bool { QString allowlistPrefix = "[ALLOWLIST ENTITY SCRIPTS]"; QList safeURLS = { "" }; - safeURLS += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts); + safeURLS += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegularExpression("\\s*,\\s*"), Qt::SkipEmptyParts); // PULL SAFEURLS FROM INTERFACE.JSON Settings QVariant raw = Setting::Handle("private/settingsSafeURLS").get(); - QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegExp("\\s*[,\r\n]+\\s*"), Qt::SkipEmptyParts); + QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegularExpression("\\s*[,\r\n]+\\s*"), Qt::SkipEmptyParts); safeURLS += settingsSafeURLS; // END PULL SAFEURLS FROM INTERFACE.JSON Settings diff --git a/interface/src/Application_Setup.cpp b/interface/src/Application_Setup.cpp index ba57063baea..fba5c6be658 100644 --- a/interface/src/Application_Setup.cpp +++ b/interface/src/Application_Setup.cpp @@ -251,7 +251,7 @@ static const int WATCHDOG_TIMER_TIMEOUT = 100; static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt"; #endif -bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { +bool Application::setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT; bool suppressPrompt = parser.isSet("suppress-settings-reset"); @@ -330,6 +330,8 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted DependencyManager::set(); DependencyManager::set(); DependencyManager::set(NodeType::Agent, listenPort); + // Octree processor requires NodeList. + _octreeProcessor = std::make_shared(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); // ModelFormatRegistry must be defined before ModelCache. See the ModelCache constructor. @@ -416,7 +418,14 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted DependencyManager::set(); - DependencyManager::set(nullptr); + { + auto octreeSceneStats = qApp->getOcteeSceneStats(); + if (!octreeSceneStats) { + qCritical() << "setupEssentials: Octree stats provider not available yet. This should never happen"; + std::abort(); + } + DependencyManager::set(nullptr, octreeSceneStats.value()); + } DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -461,7 +470,6 @@ void Application::initialize(const QCommandLineParser &parser) { _entitySimulation = std::make_shared(); _physicsEngine = std::make_shared(Vectors::ZERO); _entityClipboard = std::make_shared(); - _octreeProcessor = std::make_shared(); _entityEditSender = std::make_shared(); _graphicsEngine = std::make_shared(); _applicationOverlay = std::make_shared(); @@ -660,6 +668,14 @@ void Application::initialize(const QCommandLineParser &parser) { QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-SemiBold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-Regular.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-Medium.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Roboto-Regular.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Roboto-Bold.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Roboto-Italic.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Roboto-BoldItalic.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/RobotoMono-Regular.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/RobotoMono-Bold.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/RobotoMono-Italic.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/RobotoMono-BoldItalic.ttf"); _window->setWindowTitle("Overte"); Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us @@ -764,7 +780,8 @@ void Application::initialize(const QCommandLineParser &parser) { << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer << NodeType::EntityScriptServer); // setDefaultFormat has no effect after the platform window has been created, so call it here. - QSurfaceFormat::setDefaultFormat(getDefaultOpenGLSurfaceFormat()); + // QT6TODO: I was getting a warning when it was here, so I moved it to the beginning. + //QSurfaceFormat::setDefaultFormat(getDefaultOpenGLSurfaceFormat()); #ifdef USE_GL _primaryWidget = new GLCanvas(); @@ -878,14 +895,17 @@ void Application::initialize(const QCommandLineParser &parser) { { "gl_renderer", glContextData.renderer.c_str() }, { "ideal_thread_count", QThread::idealThreadCount() } }; +#ifdef Q_OS_MAC auto macVersion = QSysInfo::macVersion(); if (macVersion != QSysInfo::MV_None) { properties["os_osx_version"] = QSysInfo::macVersion(); } +#elifdef Q_OS_WIN auto windowsVersion = QSysInfo::windowsVersion(); if (windowsVersion != QSysInfo::WV_None) { properties["os_win_version"] = QSysInfo::windowsVersion(); } +#endif ProcessorInfo procInfo; if (getProcessorInfo(procInfo)) { @@ -1196,10 +1216,12 @@ void Application::initialize(const QCommandLineParser &parser) { properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount; properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount; - NodeToOctreeSceneStats* octreeServerSceneStats = getOcteeSceneStats(); + auto octreeServerSceneStats = getOcteeSceneStats(); unsigned long totalServerOctreeElements = 0; - for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { - totalServerOctreeElements += i->second.getTotalElements(); + if (octreeServerSceneStats) { + for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats.value()->begin(); i != octreeServerSceneStats.value()->end(); i++) { + totalServerOctreeElements += i->second.getTotalElements(); + } } properties["local_octree_elements"] = (qint64) OctreeElement::getInternalNodeCount(); @@ -2010,11 +2032,11 @@ void Application::setupSignalsAndOperators() { auto reticlePos = getApplicationCompositor().getReticlePosition(); QPoint localPos(reticlePos.x, reticlePos.y); // both hmd and desktop already handle this in our coordinates. if (state) { - QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier, getVirtualPointingDevice().get()); sendEvent(_primaryWidget, &mousePress); _reticleClickPressed = true; } else { - QMouseEvent mouseRelease(QEvent::MouseButtonRelease, localPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + QMouseEvent mouseRelease(QEvent::MouseButtonRelease, localPos, localPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier, getVirtualPointingDevice().get()); sendEvent(_primaryWidget, &mouseRelease); _reticleClickPressed = false; } diff --git a/interface/src/Application_UI.cpp b/interface/src/Application_UI.cpp index 53e8600b717..cdb1ab9aee0 100644 --- a/interface/src/Application_UI.cpp +++ b/interface/src/Application_UI.cpp @@ -84,6 +84,7 @@ #include #include #include +#include #include "AboutUtil.h" #include "ArchiveDownloadInterface.h" @@ -229,7 +230,10 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona surfaceContext->setContextProperty("offscreenFlags", flags); surfaceContext->setContextProperty("AddressManager", DependencyManager::get().data()); + // TODO: Replace Settings (conflicts with QtCore.Settings) with SettingsInterface surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext)); + surfaceContext->setContextProperty("SettingsInterface", new QMLSettingsScriptingInterface(surfaceContext)); + surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface()); @@ -258,8 +262,16 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona surfaceContext->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get()); surfaceContext->setContextProperty("Controller", DependencyManager::get().data()); surfaceContext->setContextProperty("Pointers", DependencyManager::get().data()); + + // QT6TODO: replace all of the instances of "Window" in QML with WindowScriptingInterface surfaceContext->setContextProperty("Window", DependencyManager::get().data()); + + // There's a lot of stuff in our QML called "Window", + // so this needs to be WindowScriptingInterface + surfaceContext->setContextProperty("WindowScriptingInterface", DependencyManager::get().data()); + surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); + surfaceContext->setContextProperty("PickScriptingInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("About", AboutUtil::getInstance()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated. surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); @@ -580,7 +592,7 @@ void Application::showAssetServerWidget(QString filePath) { if (!DependencyManager::get()->getThisNodeCanWriteAssets() || getLoginDialogPoppedUp()) { return; } - static const QUrl url { "hifi/AssetServer.qml" }; + static const QUrl url { "overte/compat/CompatAssetDialog.qml" }; auto startUpload = [=, this](QQmlContext* context, QObject* newObject){ if (!filePath.isEmpty()) { @@ -596,7 +608,7 @@ void Application::showAssetServerWidget(QString filePath) { if (!hmd->getShouldShowTablet() && !isHMDMode()) { getOffscreenUI()->show(url, "AssetServer", startUpload); } else { - static const QUrl url("qrc:///qml/hifi/dialogs/TabletAssetServer.qml"); + static const QUrl url("qrc:///qml/overte/dialogs/AssetDialog.qml"); if (!tablet->isPathLoaded(url)) { tablet->pushOntoStack(url); } @@ -808,7 +820,7 @@ void Application::updateThemeColors() { QPalette palette = style->standardPalette(); - if (_darkTheme.get()) { + if (_themePrefs->getDarkMode()) { palette.setColor(QPalette::Window, QColor(48, 48, 48)); palette.setColor(QPalette::WindowText, QColor(224, 224, 224)); @@ -838,16 +850,6 @@ void Application::updateThemeColors() { qApp->getPrimaryMenu()->setPalette(palette); // weird Qt bug workaround } -void Application::setDarkThemePreference(bool value) { - bool previousValue = _darkTheme.get(); - - if (value == previousValue) { return; } - - _darkTheme.set(value); - updateThemeColors(); - emit darkThemePreferenceChanged(value); -} - void Application::showVRKeyboardForHudUI(bool show) { if (show) { DependencyManager::get()->setRaised(true, true); @@ -901,6 +903,9 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Assets", DependencyManager::get().data()); surfaceContext->setContextProperty("Keyboard", DependencyManager::get().data()); + // TODO: replace instances of Keyboard with KeyboardScriptingInterface + surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get().data()); + surfaceContext->setContextProperty("AvatarList", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); @@ -912,10 +917,21 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { #endif surfaceContext->setContextProperty("Overlays", &_overlays); + + // QT6TODO: replace all of the instances of "Window" in QML with WindowScriptingInterface surfaceContext->setContextProperty("Window", DependencyManager::get().data()); + + // There's a lot of stuff in our QML called "Window", + // so this needs to be WindowScriptingInterface + surfaceContext->setContextProperty("WindowScriptingInterface", DependencyManager::get().data()); + surfaceContext->setContextProperty("Desktop", DependencyManager::get().data()); surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); + + // TODO: Replace Settings (conflicts with QtCore.Settings) with SettingsInterface surfaceContext->setContextProperty("Settings", new QMLSettingsScriptingInterface(surfaceContext)); + surfaceContext->setContextProperty("SettingsInterface", new QMLSettingsScriptingInterface(surfaceContext)); + surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get().data()); surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get().data()); @@ -941,6 +957,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Render", RenderScriptingInterface::getInstance()); surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance()); surfaceContext->setContextProperty("Workload", _gameWorkload._engine->getConfiguration().get()); + surfaceContext->setContextProperty("PickScriptingInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); surfaceContext->setContextProperty("Snapshot", DependencyManager::get().data()); @@ -981,7 +998,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { } // Download the FST file, to attempt to determine its model type - QVariantHash fstMapping = FSTReader::downloadMapping(url); + hifi::VariantMultiHash fstMapping = FSTReader::downloadMapping(url); FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping); @@ -1462,7 +1479,7 @@ void Application::addAssetToWorldError(QString modelName, QString errorText) { void Application::setMenuBarVisible(bool visible) { if (QThread::currentThread() != qApp->thread()) { - QMetaObject::invokeMethod(this, "setMenuBarVisible", Q_ARG(bool, visible)); + QMetaObject::invokeMethod(this, "setMenuBarVisible", Q_GENERIC_ARG(bool, visible)); return; } diff --git a/interface/src/ArchiveDownloadInterface.cpp b/interface/src/ArchiveDownloadInterface.cpp index d82fd0bec2e..ea187ab051e 100644 --- a/interface/src/ArchiveDownloadInterface.cpp +++ b/interface/src/ArchiveDownloadInterface.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index a394825f2ad..eaa018cfe21 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -140,7 +140,7 @@ void AvatarBookmarks::addBookmark(const QString& bookmarkName) { void AvatarBookmarks::addBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_ARG(QString, bookmarkName)); + BLOCKING_INVOKE_METHOD(this, "addBookmark", Q_GENERIC_ARG(QString, bookmarkName)); return; } QVariantMap bookmark = getAvatarDataToBookmark(); @@ -157,7 +157,7 @@ void AvatarBookmarks::saveBookmark(const QString& bookmarkName) { void AvatarBookmarks::saveBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_ARG(QString, bookmarkName)); + BLOCKING_INVOKE_METHOD(this, "saveBookmark", Q_GENERIC_ARG(QString, bookmarkName)); return; } if (contains(bookmarkName)) { @@ -174,7 +174,7 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { void AvatarBookmarks::removeBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_ARG(QString, bookmarkName)); + BLOCKING_INVOKE_METHOD(this, "removeBookmark", Q_GENERIC_ARG(QString, bookmarkName)); return; } @@ -230,7 +230,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_ARG(QString, bookmarkName)); + BLOCKING_INVOKE_METHOD(this, "loadBookmark", Q_GENERIC_ARG(QString, bookmarkName)); return; } @@ -277,7 +277,7 @@ void AvatarBookmarks::loadBookmarkInternal(const QString& bookmarkName) { void AvatarBookmarks::readFromFile() { // migrate old avatarbookmarks.json, used to be in 'local' folder on windows - QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME; + QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + AVATARBOOKMARKS_FILENAME; QFile oldConfig(oldConfigPath); // I imagine that in a year from now, this code for migrating (as well as the two lines above) @@ -307,7 +307,7 @@ QVariantMap AvatarBookmarks::getBookmarkInternal(const QString &bookmarkName) { if (QThread::currentThread() != thread()) { QVariantMap result; - BLOCKING_INVOKE_METHOD(this, "getBookmark", Q_RETURN_ARG(QVariantMap, result), Q_ARG(QString, bookmarkName)); + BLOCKING_INVOKE_METHOD(this, "getBookmark", Q_GENERIC_RETURN_ARG(QVariantMap, result), Q_GENERIC_ARG(QString, bookmarkName)); return result; } @@ -369,3 +369,50 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { bookmark.insert(ENTRY_AVATAR_ENTITIES, wearableEntities); return bookmark; } + +void AvatarBookmarks::setBookmarkData(const QString& bookmarkName, const QVariantMap& data) { + if (!ScriptPermissions::isCurrentScriptAllowed(ScriptPermissions::Permission::SCRIPT_PERMISSION_GET_AVATAR_URL)) { + return; + } + + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD( + this, + "setBookmarkData", + Q_GENERIC_ARG(QString, bookmarkName), + Q_GENERIC_ARG(QVariantMap, data) + ); + return; + } + + auto sanitizedData = data; + + if (!sanitizedData.contains(ENTRY_VERSION)) { + sanitizedData.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); + } + + if (!sanitizedData.contains(ENTRY_AVATAR_URL)) { + qCritical() << "setBookmarkData called without \"avatarUrl\" field!"; + return; + } + + if (!sanitizedData.contains(ENTRY_AVATAR_ICON)) { + sanitizedData.insert(ENTRY_AVATAR_ICON, ""); + } + + if (!sanitizedData.contains(ENTRY_AVATAR_SCALE)) { + sanitizedData.insert(ENTRY_AVATAR_SCALE, 1.0); + } + + if (!sanitizedData.contains(ENTRY_AVATAR_ENTITIES)) { + sanitizedData.insert(ENTRY_AVATAR_ENTITIES, QVariantList()); + } + + auto alreadyExists = contains(bookmarkName); + + insert(bookmarkName, sanitizedData); + + if (!alreadyExists) { + emit bookmarkAdded(bookmarkName); + } +} diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index 40d862b83ed..8eccc545fcb 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -105,6 +105,22 @@ public slots: */ QVariantMap getBookmarks(); + /*@jsdoc + * Directly sets the data object of a bookmark. + * @function AvatarBookmarks.setBookmarkData + * @param {string} - The name of the bookmark. If no bookmark with the specified name exists, it will be created. Otherwise, it will edit the existing one. + * @param {object} - The bookmark data. See the example below for valid keys and their defaults. + * @example + * AvatarBookmarks.setBookmarkData("Woody", { + * version: 3, + * avatarScale: 1.0, + * avatarEntities: [], + * avatarIcon: "", + * avatarUrl: "qrc:///meshes/defaultAvatar_full.fst", + * }); + */ + void setBookmarkData(const QString& bookmarkName, const QVariantMap& data); + signals: /*@jsdoc * Triggered when an avatar bookmark is loaded, setting your avatar model, scale, and avatar entities to those in the bookmark. diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 624d5430a26..5c64f3f81a2 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -145,7 +145,7 @@ void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply* requestReply // give that session ID to the account manager auto accountManager = DependencyManager::get(); - accountManager->setSessionID(sessionID); + accountManager->setSessionID(QUuid(sessionID)); } } diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp index 8a10e636ae3..3ac51996330 100644 --- a/interface/src/LocationBookmarks.cpp +++ b/interface/src/LocationBookmarks.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -25,7 +26,7 @@ const QString LocationBookmarks::HOME_BOOKMARK = "Home"; LocationBookmarks::LocationBookmarks() { - _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + LOCATIONBOOKMARKS_FILENAME; + _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + LOCATIONBOOKMARKS_FILENAME; readFromFile(); } @@ -104,7 +105,7 @@ void LocationBookmarks::addBookmark() { disconnect(dlg, &ModalDialogListener::response, this, nullptr); auto bookmarkName = response.toString(); - bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + bookmarkName = bookmarkName.trimmed().replace(QRegularExpression("(\r\n|[\r\n\t\v ])+"), " "); if (bookmarkName.length() == 0) { return; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a92f1b3ffd0..749b3d1abb0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -19,6 +19,7 @@ // QT6TODO: The Key Enums should be replaced by QKeyCombination in Qt6 #include "Menu.h" +#include #include #include #include @@ -97,6 +98,8 @@ Menu::Menu() { dialogsManager.data(), &DialogsManager::toggleLoginDialog); } + // Qt6 TODO: This crashes when the domain changes, maybe a thread safety thing? +#if 0 auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In"); domainLogin->setVisible(false); connect(domainLogin, &QAction::triggered, [] { @@ -107,6 +110,7 @@ Menu::Menu() { connect(domainAccountManager.data(), &DomainAccountManager::hasLogInChanged, [domainLogin](bool hasLogIn) { domainLogin->setVisible(hasLogIn); }); +#endif // File > Quit addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, static_cast(Qt::CTRL) | static_cast(Qt::Key_Q), qApp, SLOT(quit()), QAction::QuitRole); @@ -149,8 +153,8 @@ Menu::Menu() { auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, static_cast(Qt::CTRL) | static_cast(Qt::Key_J)); connect(action, &QAction::triggered, [] { if (!qApp->getLoginDialogPoppedUp()) { - static const QUrl widgetUrl("hifi/dialogs/RunningScripts.qml"); - static const QUrl tabletUrl("hifi/dialogs/TabletRunningScripts.qml"); + static const QUrl widgetUrl("overte/compat/RunningScripts_Window.qml"); + static const QUrl tabletUrl("overte/dialogs/RunningScriptsDialog.qml"); static const QString name("RunningScripts"); qApp->showDialog(widgetUrl, tabletUrl, name); } @@ -255,8 +259,11 @@ Menu::Menu() { // Settings menu ---------------------------------- MenuWrapper* settingsMenu = addMenu("Settings"); + // Legacy settings, some stuff isn't accessible from the new one yet + MenuWrapper *legacySettingsMenu = settingsMenu->addMenu("Legacy"); + // Settings > General... - action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, static_cast(Qt::CTRL) | static_cast(Qt::Key_G), nullptr, nullptr); + action = addActionToQMenuAndActionHash(legacySettingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_G, nullptr, nullptr); connect(action, &QAction::triggered, [] { if (!qApp->getLoginDialogPoppedUp()) { qApp->showDialog(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), @@ -265,7 +272,7 @@ Menu::Menu() { }); // Settings > Controls... - action = addActionToQMenuAndActionHash(settingsMenu, "Controls..."); + action = addActionToQMenuAndActionHash(legacySettingsMenu, "Controls..."); connect(action, &QAction::triggered, [] { auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); auto hmd = DependencyManager::get(); @@ -277,7 +284,7 @@ Menu::Menu() { }); // Settings > Audio... - action = addActionToQMenuAndActionHash(settingsMenu, "Audio..."); + action = addActionToQMenuAndActionHash(legacySettingsMenu, "Audio..."); connect(action, &QAction::triggered, [] { static const QUrl tabletUrl("hifi/audio/Audio.qml"); auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index e6f7c508408..56e1199badb 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -23,6 +23,7 @@ #include "ModelSelector.h" #include "ModelPropertiesDialog.h" #include "InterfaceLogging.h" +#include "shared/QtHelpers.h" static const int MAX_TEXTURE_SIZE = 8192; @@ -109,7 +110,7 @@ bool ModelPackager::loadModel() { qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _hfmModel = FBXSerializer().read(fbxContents, QVariantHash(), _fbxInfo.filePath()); + _hfmModel = FBXSerializer().read(fbxContents, hifi::VariantMultiHash(), _fbxInfo.filePath()); // make sure we have some basic mappings populateBasicMapping(_mapping, _fbxInfo.filePath(), *_hfmModel); @@ -233,7 +234,7 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const hfm::Model& hfmModel) { +void ModelPackager::populateBasicMapping(hifi::VariantMultiHash& mapping, QString filename, const hfm::Model& hfmModel) { // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file @@ -349,7 +350,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename blendshapes.insert("Sneer", QVariantList() << "NoseScrunch_Right" << 0.75); blendshapes.insert("Sneer", QVariantList() << "Squint_Left" << 0.5); blendshapes.insert("Sneer", QVariantList() << "Squint_Right" << 0.5); - mapping.insert(BLENDSHAPE_FIELD, blendshapes); + mapping.insert(BLENDSHAPE_FIELD, qMultiHashToQVariant(blendshapes)); } } diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 9a2a49562a6..245a5632d90 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -34,7 +34,7 @@ class ModelPackager : public QObject { bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const hfm::Model& hfmModel); + void populateBasicMapping(hifi::VariantMultiHash& mapping, QString filename, const hfm::Model& hfmModel); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index d67341990db..33c39fcb3c6 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -25,8 +25,10 @@ #include #include +#include "shared/QtHelpers.h" -ModelPropertiesDialog::ModelPropertiesDialog(const QVariantHash& originalMapping, + +ModelPropertiesDialog::ModelPropertiesDialog(const hifi::VariantMultiHash& originalMapping, const QString& basePath, const HFMModel& hfmModel) : _originalMapping(originalMapping), _basePath(basePath), @@ -70,8 +72,8 @@ _hfmModel(hfmModel) reset(); } -QVariantHash ModelPropertiesDialog::getMapping() const { - QVariantHash mapping = _originalMapping; +hifi::VariantMultiHash ModelPropertiesDialog::getMapping() const { + hifi::VariantMultiHash mapping = _originalMapping; mapping.insert(TYPE_FIELD, FSTReader::getNameFromType(FSTReader::HEAD_AND_BODY_MODEL)); mapping.insert(NAME_FIELD, _name->text()); mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); @@ -85,7 +87,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { } mapping.insert(JOINT_INDEX_FIELD, jointIndices); - QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); + hifi::VariantMultiHash joints = qVariantToQMultiHash(mapping.value(JOINT_FIELD)); insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); @@ -97,7 +99,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); - mapping.insert(JOINT_FIELD, joints); + mapping.insert(JOINT_FIELD, qMultiHashToQVariant(joints)); return mapping; } @@ -177,7 +179,7 @@ QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const { return box; } -void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { +void ModelPropertiesDialog::insertJointMapping(hifi::VariantMultiHash& joints, const QString& joint, const QString& name) const { if (_hfmModel.jointIndices.contains(name)) { joints.insert(joint, name); } else { diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index 8cf9bd52483..cb6aadaaa28 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -29,10 +29,10 @@ class ModelPropertiesDialog : public QDialog { Q_OBJECT public: - ModelPropertiesDialog(const QVariantHash& originalMapping, + ModelPropertiesDialog(const hifi::VariantMultiHash& originalMapping, const QString& basePath, const HFMModel& hfmModel); - QVariantHash getMapping() const; + hifi::VariantMultiHash getMapping() const; private slots: void reset(); @@ -43,9 +43,9 @@ private slots: private: QComboBox* createJointBox(bool withNone = true) const; QDoubleSpinBox* createTranslationBox() const; - void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; + void insertJointMapping(hifi::VariantMultiHash& joints, const QString& joint, const QString& name) const; - QVariantHash _originalMapping; + hifi::VariantMultiHash _originalMapping; QString _basePath; HFMModel _hfmModel; QLineEdit* _name = nullptr; diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 6da9327caca..e855546f071 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -17,6 +17,7 @@ #include #include #include +#include ModelSelector::ModelSelector() { QFormLayout* form = new QFormLayout(this); @@ -53,7 +54,7 @@ void ModelSelector::browse() { "Model files (*.fst *.fbx)"); QFileInfo fileInfo(filename); - if (fileInfo.isFile() && fileInfo.completeSuffix().contains(QRegExp("fst|fbx|FST|FBX"))) { + if (fileInfo.isFile() && fileInfo.completeSuffix().contains(QRegularExpression("fst|fbx|FST|FBX"))) { _modelFile = fileInfo; _browseButton->setText(fileInfo.fileName()); lastModelBrowseLocation.set(fileInfo.path()); diff --git a/interface/src/ScriptHighlighting.cpp b/interface/src/ScriptHighlighting.cpp index 1d31f2823b8..a9c0e7500b6 100644 --- a/interface/src/ScriptHighlighting.cpp +++ b/interface/src/ScriptHighlighting.cpp @@ -15,14 +15,14 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) : QSyntaxHighlighter(parent) { - _keywordRegex = QRegExp("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b"); - _quotedTextRegex = QRegExp("(\"[^\"]*(\"){0,1}|\'[^\']*(\'){0,1})"); - _multiLineCommentBegin = QRegExp("/\\*"); - _multiLineCommentEnd = QRegExp("\\*/"); - _numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}"); - _singleLineComment = QRegExp("//[^\n]*"); - _truefalseRegex = QRegExp("\\b(true|false)\\b"); - _alphacharRegex = QRegExp("[A-Za-z]"); + _keywordRegex = QRegularExpression("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b"); + _quotedTextRegex = QRegularExpression("(\"[^\"]*(\"){0,1}|\'[^\']*(\'){0,1})"); + _multiLineCommentBegin = QRegularExpression("/\\*"); + _multiLineCommentEnd = QRegularExpression("\\*/"); + _numberRegex = QRegularExpression("[0-9]+(\\.[0-9]+){0,1}"); + _singleLineComment = QRegularExpression("//[^\n]*"); + _truefalseRegex = QRegularExpression("\\b(true|false)\\b"); + _alphacharRegex = QRegularExpression("[A-Za-z]"); } void ScriptHighlighting::highlightBlock(const QString& text) { @@ -34,11 +34,12 @@ void ScriptHighlighting::highlightBlock(const QString& text) { } void ScriptHighlighting::highlightKeywords(const QString& text) { - int index = _keywordRegex.indexIn(text); - while (index >= 0) { - int length = _keywordRegex.matchedLength(); - setFormat(index, length, Qt::blue); - index = _keywordRegex.indexIn(text, index + length); + auto keywordMatch = _keywordRegex.match(text); + while (keywordMatch.hasMatch()) { + qsizetype index = keywordMatch.capturedStart(); + qsizetype length = keywordMatch.capturedLength(); + setFormat(static_cast(index), static_cast(length), Qt::blue); + keywordMatch = _keywordRegex.match(text, index + length); } } @@ -46,11 +47,12 @@ void ScriptHighlighting::formatComments(const QString& text) { setCurrentBlockState(BlockStateClean); - int start = (previousBlockState() != BlockStateInMultiComment) ? text.indexOf(_multiLineCommentBegin) : 0; + qsizetype start = (previousBlockState() != BlockStateInMultiComment) ? text.indexOf(_multiLineCommentBegin) : 0; while (start > -1) { - int end = text.indexOf(_multiLineCommentEnd, start); - int length = (end == -1 ? text.length() : (end + _multiLineCommentEnd.matchedLength())) - start; + auto multiLineCommentEndMatch = _multiLineCommentEnd.match(text, start); + qsizetype end = multiLineCommentEndMatch.capturedEnd(); + qsizetype length = (end == qsizetype(-1) ? text.length() : (end + multiLineCommentEndMatch.capturedLength())) - start; setFormat(start, length, Qt::lightGray); start = text.indexOf(_multiLineCommentBegin, start + length); if (end == -1) { @@ -58,53 +60,63 @@ void ScriptHighlighting::formatComments(const QString& text) { } } - int index = _singleLineComment.indexIn(text); + auto singleLineCommentMatch = _singleLineComment.match(text); + qsizetype index = singleLineCommentMatch.capturedStart(); while (index >= 0) { - int length = _singleLineComment.matchedLength(); - int quoted_index = _quotedTextRegex.indexIn(text); + qsizetype length = singleLineCommentMatch.capturedLength(); + auto quotedTextRegexMatch = _quotedTextRegex.match(text); + qsizetype quoted_index = quotedTextRegexMatch.capturedStart(); bool valid = true; while (quoted_index >= 0 && valid) { - int quoted_length = _quotedTextRegex.matchedLength(); + qsizetype quoted_length = quotedTextRegexMatch.capturedLength(); if (quoted_index <= index && index <= (quoted_index + quoted_length)) { valid = false; } - quoted_index = _quotedTextRegex.indexIn(text, quoted_index + quoted_length); + quotedTextRegexMatch = _quotedTextRegex.match(text, quoted_index + quoted_length); + quoted_index = quotedTextRegexMatch.capturedStart(); } if (valid) { setFormat(index, length, Qt::lightGray); } - index = _singleLineComment.indexIn(text, index + length); + singleLineCommentMatch = _singleLineComment.match(text, index + length); + index = singleLineCommentMatch.capturedStart(); } } void ScriptHighlighting::formatQuotedText(const QString& text){ - int index = _quotedTextRegex.indexIn(text); + auto quotedTextRegexMatch = _quotedTextRegex.match(text); + qsizetype index = quotedTextRegexMatch.capturedStart();; while (index >= 0) { - int length = _quotedTextRegex.matchedLength(); + qsizetype length = quotedTextRegexMatch.capturedLength(); setFormat(index, length, Qt::red); - index = _quotedTextRegex.indexIn(text, index + length); + quotedTextRegexMatch = _quotedTextRegex.match(text, index + length); + index = quotedTextRegexMatch.capturedStart(); } } void ScriptHighlighting::formatNumbers(const QString& text){ - int index = _numberRegex.indexIn(text); + auto numberRegexMatch = _numberRegex.match(text); + qsizetype index = numberRegexMatch.capturedStart(); while (index >= 0) { - int length = _numberRegex.matchedLength(); - if (index == 0 || _alphacharRegex.indexIn(text, index - 1) != (index - 1)) { + qsizetype length = numberRegexMatch.capturedLength(); + if (index == 0 || text.indexOf(_alphacharRegex, index - 1) != (index - 1)) { setFormat(index, length, Qt::green); } - index = _numberRegex.indexIn(text, index + length); + numberRegexMatch = _numberRegex.match(text, index + length); + index = numberRegexMatch.capturedStart(); } } void ScriptHighlighting::formatTrueFalse(const QString& text){ - int index = _truefalseRegex.indexIn(text); + auto trueFalseRegexMatch = _truefalseRegex.match(text); + qsizetype index = trueFalseRegexMatch.capturedStart(); while (index >= 0) { - int length = _truefalseRegex.matchedLength(); + qsizetype length = trueFalseRegexMatch.capturedLength(); QFont* font = new QFont(this->document()->defaultFont()); font->setBold(true); setFormat(index, length, *font); - index = _truefalseRegex.indexIn(text, index + length); + trueFalseRegexMatch = _truefalseRegex.match(text, index + length); + index = trueFalseRegexMatch.capturedStart(); } } diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h index 8952b1bcbd2..cfab657f3d0 100644 --- a/interface/src/ScriptHighlighting.h +++ b/interface/src/ScriptHighlighting.h @@ -13,6 +13,7 @@ #define hifi_ScriptHighlighting_h #include +#include class ScriptHighlighting : public QSyntaxHighlighter { Q_OBJECT @@ -34,14 +35,14 @@ class ScriptHighlighting : public QSyntaxHighlighter { void formatTrueFalse(const QString& text); private: - QRegExp _alphacharRegex; - QRegExp _keywordRegex; - QRegExp _quotedTextRegex; - QRegExp _multiLineCommentBegin; - QRegExp _multiLineCommentEnd; - QRegExp _numberRegex; - QRegExp _singleLineComment; - QRegExp _truefalseRegex; + QRegularExpression _alphacharRegex; + QRegularExpression _keywordRegex; + QRegularExpression _quotedTextRegex; + QRegularExpression _multiLineCommentBegin; + QRegularExpression _multiLineCommentEnd; + QRegularExpression _numberRegex; + QRegularExpression _singleLineComment; + QRegularExpression _truefalseRegex; }; #endif // hifi_ScriptHighlighting_h diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index 463034527ac..f7eac000181 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -21,8 +21,8 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second Q_OBJECT Q_PROPERTY(QUuid attachedEntityId MEMBER attachedEntityId NOTIFY dirty) // entity whose properties define camera position and orientation Q_PROPERTY(QUuid portalEntranceEntityId MEMBER portalEntranceEntityId NOTIFY dirty) // entity whose properties define a portal's entrance position and orientation - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) // of viewpoint to render from - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) // of viewpoint to render from + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> position READ getPosition WRITE setPosition) // of viewpoint to render from + Q_PROPERTY(glm::qua orientation READ getOrientation WRITE setOrientation) // of viewpoint to render from Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees. Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters. Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters. @@ -31,8 +31,8 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second public: QUuid attachedEntityId; QUuid portalEntranceEntityId; - glm::vec3 position; - glm::quat orientation; + glm::vec<3,float,glm::packed_highp> position; + glm::qua orientation; float vFoV { DEFAULT_FIELD_OF_VIEW_DEGREES }; float nearClipPlaneDistance { DEFAULT_NEAR_CLIP }; float farClipPlaneDistance { DEFAULT_FAR_CLIP }; @@ -45,10 +45,10 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second signals: void dirty(); public slots: - glm::vec3 getPosition() { return position; } - void setPosition(glm::vec3 pos); - glm::quat getOrientation() { return orientation; } - void setOrientation(glm::quat orient); + glm::vec<3,float,glm::packed_highp> getPosition() { return position; } + void setPosition(glm::vec<3,float,glm::packed_highp> pos); + glm::qua getOrientation() { return orientation; } + void setOrientation(glm::qua orient); void enableSecondaryCameraRenderConfigs(bool enabled); void resetSizeSpectatorCamera(int width, int height); }; diff --git a/interface/src/ThemePrefs.cpp b/interface/src/ThemePrefs.cpp new file mode 100644 index 00000000000..e201375e73f --- /dev/null +++ b/interface/src/ThemePrefs.cpp @@ -0,0 +1,110 @@ +// +// ThemePrefs.cpp +// interface/src +// +// Created by Ada on 2026-02-22 +// Copyright 2026 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include + +#include "Application.h" +#include "ThemePrefs.h" + +bool ThemePrefs::getDarkMode() const { + QMutexLocker lock(&_mutex); + + if (_useSystemColorScheme.get()) { + const auto* styleHints = qApp->styleHints(); + // default to dark theme if qt doesn't know what to use + return styleHints->colorScheme() != Qt::ColorScheme::Light; + } else { + return _darkMode.get(); + } +} + +bool ThemePrefs::getHighContrast() const { + QMutexLocker lock(&_mutex); + + if (_useSystemContrast.get()) { + const auto* a11yHints = qApp->styleHints()->accessibility(); + return a11yHints->contrastPreference() == Qt::ContrastPreference::HighContrast; + } else { + return _highContrast.get(); + } +} + +bool ThemePrefs::getReducedMotion() const { + QMutexLocker lock(&_mutex); + return _reducedMotion.get(); +} + +bool ThemePrefs::getUseSystemColorScheme() const { + QMutexLocker lock(&_mutex); + return _useSystemColorScheme.get(); +} + +bool ThemePrefs::getUseSystemContrast() const { + QMutexLocker lock(&_mutex); + return _useSystemContrast.get(); +} + +void ThemePrefs::setDarkMode(bool value) { + QMutexLocker lock(&_mutex); + + auto old = _darkMode.get(); + + if (value != old) { + _darkMode.set(value); + emit themeChanged(); + } +} + +void ThemePrefs::setHighContrast(bool value) { + QMutexLocker lock(&_mutex); + + auto old = _highContrast.get(); + + if (old != value) { + _highContrast.set(value); + emit themeChanged(); + } +} + +void ThemePrefs::setReducedMotion(bool value) { + QMutexLocker lock(&_mutex); + + auto old = _reducedMotion.get(); + + if (old != value) { + _reducedMotion.set(value); + emit themeChanged(); + } +} + +void ThemePrefs::setUseSystemColorScheme(bool value) { + QMutexLocker lock(&_mutex); + + auto old = _useSystemColorScheme.get(); + + if (old != value) { + _useSystemColorScheme.set(value); + emit themeChanged(); + } +} + +void ThemePrefs::setUseSystemContrast(bool value) { + QMutexLocker lock(&_mutex); + + auto old = _useSystemContrast.get(); + + if (old != value) { + _useSystemContrast.set(value); + emit themeChanged(); + } +} diff --git a/interface/src/ThemePrefs.h b/interface/src/ThemePrefs.h new file mode 100644 index 00000000000..d6b774bd91c --- /dev/null +++ b/interface/src/ThemePrefs.h @@ -0,0 +1,64 @@ +// +// ThemePrefs.h +// interface/src +// +// Created by Ada on 2026-02-22 +// Copyright 2026 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 +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_ThemePrefs_h +#define hifi_ThemePrefs_h + +#include + +#include + +class ThemePrefs : public QObject { + Q_OBJECT + Q_PROPERTY(bool darkMode READ getDarkMode WRITE setDarkMode NOTIFY themeChanged) + Q_PROPERTY(bool highContrast READ getHighContrast WRITE setHighContrast NOTIFY themeChanged) + Q_PROPERTY(bool reducedMotion READ getReducedMotion WRITE setReducedMotion NOTIFY themeChanged) + + Q_PROPERTY(bool useSystemColorScheme READ getUseSystemColorScheme WRITE setUseSystemColorScheme NOTIFY themeChanged) + Q_PROPERTY(bool useSystemContrast READ getUseSystemContrast WRITE setUseSystemContrast NOTIFY themeChanged) + +public: + ThemePrefs(QObject* parent = nullptr) : QObject(parent) {} + + // returns the appropriate values depending on whether we're + // using the system preferences or custom ones + bool getDarkMode() const; + bool getHighContrast() const; + bool getReducedMotion() const; + + bool getUseSystemColorScheme() const; + bool getUseSystemContrast() const; + + void setDarkMode(bool value); + void setHighContrast(bool value); + void setReducedMotion(bool value); + + void setUseSystemColorScheme(bool value); + void setUseSystemContrast(bool value); + +signals: + void themeChanged(); + +private: + mutable QRecursiveMutex _mutex; + + Setting::Handle _darkMode { "Theme/darkMode", true }; + Setting::Handle _highContrast { "Theme/highContrast", true }; + Setting::Handle _reducedMotion { "Theme/reducedMotion", true }; + Setting::Handle _useSystemColorScheme { "Theme/useSystemColorScheme", true }; + Setting::Handle _useSystemContrast { "Theme/useSystemContrast", true }; + // It looks like Qt doesn't have a system reduced motion setting? + // KDE has one, but it modifies an internal KDE setting, not a Qt one. + //Setting::Handle _useSystemReducedMotion { "Theme/useSystemReducedMotion", true }; +}; + +#endif // hifi_ThemePrefs_h diff --git a/interface/src/WindowsSystemInfo.h b/interface/src/WindowsSystemInfo.h index 7c8a1d3e270..3a83ae2764e 100644 --- a/interface/src/WindowsSystemInfo.h +++ b/interface/src/WindowsSystemInfo.h @@ -185,7 +185,7 @@ void initCpuUsage() { memcpy(&lastUserCPU, &fuser, sizeof(FILETIME)); PdhOpenQuery(NULL, NULL, &cpuQuery); - PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); + PdhAddCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); PdhCollectQueryData(cpuQuery); } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 36b1020990a..c7d206b1eb2 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -378,7 +378,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { } ok = true; - holderID = EntityDynamicInterface::extractStringArgument("hold", arguments, "holderID", ok, false); + holderID = QUuid(EntityDynamicInterface::extractStringArgument("hold", arguments, "holderID", ok, false)); if (!ok) { auto myAvatar = DependencyManager::get()->getMyAvatar(); holderID = myAvatar->getSessionUUID(); diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index a46652c7d9b..4af52489a11 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -90,8 +90,8 @@ void AvatarDoctor::startDiagnosing() { return; } _model = resource; - const auto model = resource.data(); - const auto avatarModel = resource.data()->getHFMModel(); + const auto model = resource.get(); + const auto avatarModel = resource.get()->getHFMModel(); if (!avatarModel.originalURL.toLower().endsWith(".fbx")) { addError("Unsupported avatar model format.", "unsupported-format"); emit complete(getErrors()); @@ -230,7 +230,7 @@ void AvatarDoctor::startDiagnosing() { auto mapping = resource->getMapping(); - if (mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) { + if (mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].typeId() == QMetaType::QVariantHash) { const auto& jointNameMappings = mapping[JOINT_NAME_MAPPING_FIELD].toHash(); QStringList jointValues; for (const auto& jointVariant: jointNameMappings.values()) { @@ -277,7 +277,7 @@ void AvatarDoctor::startDiagnosing() { if (materialMappingResource->isLoaded()) { materialMappingHandled(); } else { - connect(materialMappingResource.data(), &NetworkTexture::finished, this, + connect(materialMappingResource.get(), &NetworkTexture::finished, this, [materialMappingHandled](bool success) mutable { materialMappingHandled(); @@ -297,7 +297,7 @@ void AvatarDoctor::startDiagnosing() { if (resource->isLoaded()) { resourceLoaded(!resource->isFailed()); } else { - connect(resource.data(), &GeometryResource::finished, this, resourceLoaded); + connect(resource.get(), &GeometryResource::finished, this, resourceLoaded); } } else { addError("Model file cannot be opened", "missing-file"); @@ -306,8 +306,8 @@ void AvatarDoctor::startDiagnosing() { } void AvatarDoctor::diagnoseTextures() { - const auto model = _model.data(); - const auto avatarModel = _model.data()->getHFMModel(); + const auto model = _model.get(); + const auto avatarModel = _model.get()->getHFMModel(); QVector externalTextures{}; QVector textureNames{}; int texturesFound = 0; @@ -344,7 +344,7 @@ void AvatarDoctor::diagnoseTextures() { } for (const auto& materialMapping : model->getMaterialMapping()) { - for (const auto& networkMaterial : materialMapping.second.data()->parsedMaterials.networkMaterials) { + for (const auto& networkMaterial : materialMapping.second.get()->parsedMaterials.networkMaterials) { texturesFound += (int)networkMaterial.second->getTextureMaps().size(); } } @@ -405,7 +405,7 @@ void AvatarDoctor::diagnoseTextures() { if (textureResource->isLoaded()) { textureLoaded(!textureResource->isFailed()); } else { - connect(textureResource.data(), &NetworkTexture::finished, this, textureLoaded); + connect(textureResource.get(), &NetworkTexture::finished, this, textureLoaded); } } else { _missingTextureCount++; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6a4b7955f0d..e4dbabcf349 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -768,11 +768,11 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic RayToAvatarIntersectionResult result; if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(const_cast(this), "findRayIntersectionVector", - Q_RETURN_ARG(RayToAvatarIntersectionResult, result), - Q_ARG(const PickRay&, ray), - Q_ARG(const QVector&, avatarsToInclude), - Q_ARG(const QVector&, avatarsToDiscard), - Q_ARG(bool, pickAgainstMesh)); + Q_GENERIC_RETURN_ARG(RayToAvatarIntersectionResult, result), + Q_GENERIC_ARG(const PickRay&, ray), + Q_GENERIC_ARG(const QVector&, avatarsToInclude), + Q_GENERIC_ARG(const QVector&, avatarsToDiscard), + Q_GENERIC_ARG(bool, pickAgainstMesh)); return result; } @@ -897,10 +897,10 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector ParabolaToAvatarIntersectionResult result; if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(const_cast(this), "findParabolaIntersectionVector", - Q_RETURN_ARG(ParabolaToAvatarIntersectionResult, result), - Q_ARG(const PickParabola&, pick), - Q_ARG(const QVector&, avatarsToInclude), - Q_ARG(const QVector&, avatarsToDiscard)); + Q_GENERIC_RETURN_ARG(ParabolaToAvatarIntersectionResult, result), + Q_GENERIC_ARG(const PickParabola&, pick), + Q_GENERIC_ARG(const QVector&, avatarsToInclude), + Q_GENERIC_ARG(const QVector&, avatarsToDiscard)); return result; } diff --git a/interface/src/avatar/AvatarProject.cpp b/interface/src/avatar/AvatarProject.cpp index cf41c0a0407..6a73ea59a25 100644 --- a/interface/src/avatar/AvatarProject.cpp +++ b/interface/src/avatar/AvatarProject.cpp @@ -110,7 +110,7 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder, try { const QByteArray fbxContents = fbx.readAll(); - hfmModel = FBXSerializer().read(fbxContents, QVariantHash(), fbxInfo.filePath()); + hfmModel = FBXSerializer().read(fbxContents, hifi::VariantMultiHash(), fbxInfo.filePath()); } catch (const QString& error) { Q_UNUSED(error) status = AvatarProjectStatus::ERROR_CREATE_READ_MODEL; diff --git a/interface/src/avatar/DetailedMotionState.cpp b/interface/src/avatar/DetailedMotionState.cpp index a22d533bc95..4ea1d40b525 100644 --- a/interface/src/avatar/DetailedMotionState.cpp +++ b/interface/src/avatar/DetailedMotionState.cpp @@ -133,7 +133,7 @@ const QUuid DetailedMotionState::getObjectID() const { } QString DetailedMotionState::getName() const { - return _avatar->getName() + "_" + _jointIndex; + return _avatar->getName() + "_" + QString::number(_jointIndex); } // virtual diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c4b000a802b..0e14bd16353 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1246,7 +1246,7 @@ void MyAvatar::restoreHandAnimation(bool isLeft) { QStringList MyAvatar::getAnimationRoles() { if (QThread::currentThread() != thread()) { QStringList result; - BLOCKING_INVOKE_METHOD(this, "getAnimationRoles", Q_RETURN_ARG(QStringList, result)); + BLOCKING_INVOKE_METHOD(this, "getAnimationRoles", Q_GENERIC_RETURN_ARG(QStringList, result)); return result; } return _skeletonModel->getRig().getAnimationRoles(); @@ -2016,7 +2016,13 @@ void MyAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArray& entit bool changed = false; _avatarEntitiesLock.withWriteLock([&] { - auto data = QJsonDocument(QCborValue::fromCbor(entityData).toJsonValue().toObject()); + QCborParserError cborError; + auto cbor = QCborValue::fromCbor(entityData, &cborError); + if (cborError.error != QCborError::NoError) { + qWarning() << "MyAvatar::updateAvatarEntity: corrupted cbor" << QString(entityData.toHex()); + return; + } + auto data = QJsonDocument(cbor.toJsonValue().toObject()); if (data.isEmpty() || data.isNull() || !data.isObject() || data.object().isEmpty()) { qDebug() << "ERROR! Trying to update with invalid avatar entity data. Skipping." << data; } else { @@ -2313,7 +2319,7 @@ const float SCRIPT_PRIORITY = 1.0f + 1.0f; const float RECORDER_PRIORITY = 1.0f + 1.0f; void MyAvatar::setJointRotations(const QVector& jointRotations) { - int numStates = glm::min(_skeletonModel->getJointStateCount(), jointRotations.size()); + int numStates = glm::min(static_cast(_skeletonModel->getJointStateCount()), jointRotations.size()); for (int i = 0; i < numStates; ++i) { // HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here _skeletonModel->setJointRotation(i, true, jointRotations[i], RECORDER_PRIORITY); @@ -2624,8 +2630,8 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "useFullAvatarURL", - Q_ARG(const QUrl&, fullAvatarURL), - Q_ARG(const QString&, modelName)); + Q_GENERIC_ARG(const QUrl&, fullAvatarURL), + Q_GENERIC_ARG(const QString&, modelName)); return; } @@ -4088,7 +4094,7 @@ bool MyAvatar::safeLanding(const glm::vec3& position) { if (QThread::currentThread() != thread()) { bool result; - BLOCKING_INVOKE_METHOD(this, "safeLanding", Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position)); + BLOCKING_INVOKE_METHOD(this, "safeLanding", Q_GENERIC_RETURN_ARG(bool, result), Q_GENERIC_ARG(const glm::vec3&, position)); return result; } glm::vec3 better; @@ -6162,7 +6168,7 @@ QVariantMap MyAvatar::getFlowData() { QVariantMap result; if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "getFlowData", - Q_RETURN_ARG(QVariantMap, result)); + Q_GENERIC_RETURN_ARG(QVariantMap, result)); return result; } if (_skeletonModel->isLoaded()) { @@ -6230,7 +6236,7 @@ QVariantList MyAvatar::getCollidingFlowJoints() { QVariantList result; if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "getCollidingFlowJoints", - Q_RETURN_ARG(QVariantList, result)); + Q_GENERIC_RETURN_ARG(QVariantList, result)); return result; } @@ -6549,7 +6555,7 @@ void MyAvatar::updateHeadLookAt(float deltaTime) { void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "setHeadLookAt", - Q_ARG(const glm::vec3&, lookAtTarget)); + Q_GENERIC_ARG(const glm::vec3&, lookAtTarget)); return; } _headLookAtActive = true; @@ -6561,7 +6567,7 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) { void MyAvatar::setEyesLookAt(const glm::vec3& lookAtTarget) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "setEyesLookAt", - Q_ARG(const glm::vec3&, lookAtTarget)); + Q_GENERIC_ARG(const glm::vec3&, lookAtTarget)); return; } _eyesLookAtTarget.set(lookAtTarget); @@ -6658,8 +6664,8 @@ bool MyAvatar::getHMDCrouchRecenterEnabled() const { bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "setPointAt", Q_RETURN_ARG(bool, result), - Q_ARG(const glm::vec3&, pointAtTarget)); + BLOCKING_INVOKE_METHOD(this, "setPointAt", Q_GENERIC_RETURN_ARG(bool, result), + Q_GENERIC_ARG(const glm::vec3&, pointAtTarget)); return result; } if (_skeletonModel->isLoaded() && _pointAtActive) { @@ -6689,3 +6695,37 @@ float MyAvatar::getCameraSensitivity() const { void MyAvatar::setCameraSensitivity(float cameraSensitivity) { qApp->getCamera().setSensitivity(cameraSensitivity); } + +MyAvatar::TabletInputMode MyAvatar::getTabletInputMode() { + auto prefersStylus = qApp->getPreferStylusOverLaser(); + auto prefersFinger = qApp->getPreferAvatarFingerOverStylus(); + + if (prefersFinger) { + return TabletInputMode::AvatarFingers; + } else if (prefersStylus) { + return TabletInputMode::Styluses; + } else { + return TabletInputMode::Lasers; + } +} + +void MyAvatar::setTabletInputMode(MyAvatar::TabletInputMode mode) { + switch (mode) { + case TabletInputMode::Lasers: + qApp->setPreferStylusOverLaser(false); + qApp->setPreferAvatarFingerOverStylus(false); + break; + + case TabletInputMode::Styluses: + qApp->setPreferStylusOverLaser(true); + qApp->setPreferAvatarFingerOverStylus(false); + break; + + case TabletInputMode::AvatarFingers: + qApp->setPreferStylusOverLaser(true); + qApp->setPreferAvatarFingerOverStylus(true); + break; + } + + emit tabletInputModeChanged(mode); +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index fa8c7153d44..f911113a450 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -326,9 +326,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) QVector3D getQmlPosition() { auto p = getWorldPosition(); return QVector3D(p.x, p.y, p.z); } - Q_PROPERTY(glm::vec3 feetPosition READ getWorldFeetPosition WRITE goToFeetLocation) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> feetPosition READ getWorldFeetPosition WRITE goToFeetLocation) Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) - Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame) Q_PROPERTY(QString motorMode READ getScriptedMotorMode WRITE setScriptedMotorMode) @@ -337,18 +337,18 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode audioListenerModeHead READ getAudioListenerModeHead) Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) - Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition) - Q_PROPERTY(glm::quat customListenOrientation READ getCustomListenOrientation WRITE setCustomListenOrientation) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition) + Q_PROPERTY(glm::qua customListenOrientation READ getCustomListenOrientation WRITE setCustomListenOrientation) Q_PROPERTY(float rotationRecenterFilterLength READ getRotationRecenterFilterLength WRITE setRotationRecenterFilterLength) Q_PROPERTY(float rotationThreshold READ getRotationThreshold WRITE setRotationThreshold) Q_PROPERTY(bool enableStepResetRotation READ getEnableStepResetRotation WRITE setEnableStepResetRotation) Q_PROPERTY(bool enableDrawAverageFacing READ getEnableDrawAverageFacing WRITE setEnableDrawAverageFacing) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) - Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) - Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) - Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition) - Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> leftHandPosition READ getLeftHandPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> rightHandPosition READ getRightHandPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> leftHandTipPosition READ getLeftHandTipPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> rightHandTipPosition READ getRightHandTipPosition) Q_PROPERTY(controller::Pose leftHandPose READ getLeftHandPose) Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose) @@ -388,6 +388,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool allowTeleporting READ getAllowTeleporting) Q_PROPERTY(float cameraBoomLength MEMBER _boomLength) + Q_PROPERTY(MyAvatar::TabletInputMode tabletInputMode READ getTabletInputMode WRITE setTabletInputMode NOTIFY tabletInputModeChanged) + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; const QString DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE = "head"; @@ -563,6 +565,13 @@ class MyAvatar : public Avatar { static const std::array allowAvatarStandingPreferenceStrings; static const std::array allowAvatarLeaningPreferenceStrings; + enum class TabletInputMode : uint { + Lasers, + Styluses, + AvatarFingers, + }; + Q_ENUM(TabletInputMode); + explicit MyAvatar(QThread* thread); virtual ~MyAvatar(); @@ -604,9 +613,9 @@ class MyAvatar : public Avatar { virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override; void preDisplaySide(const RenderArgs* renderArgs); - const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; } - const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; } - const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } + const glm::mat<4,4,float,glm::packed_highp>& getHMDSensorMatrix() const { return _hmdSensorMatrix; } + const glm::vec<3,float,glm::packed_highp>& getHMDSensorPosition() const { return _hmdSensorPosition; } + const glm::qua& getHMDSensorOrientation() const { return _hmdSensorOrientation; } /*@jsdoc * Gets the avatar orientation. Suitable for use in QML. @@ -623,18 +632,18 @@ class MyAvatar : public Avatar { Q_INVOKABLE QVariant getOrientationVar() const; // A method intended to be overriden by MyAvatar for polling orientation for network transmission. - glm::quat getOrientationOutbound() const override; + glm::qua getOrientationOutbound() const override; // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. - void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); + void updateFromHMDSensorMatrix(const glm::mat<4,4,float,glm::packed_highp>& hmdSensorMatrix); // compute the hip to hand average azimuth. - glm::vec2 computeHandAzimuth() const; + glm::vec<2,float,glm::packed_highp> computeHandAzimuth() const; // read the location of a hand controller and save the transform - void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); + void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache>& matrixCache); // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. @@ -653,7 +662,7 @@ class MyAvatar : public Avatar { * var defaultEyePosition = MyAvatar.getDefaultEyePosition(); * print(JSON.stringify(defaultEyePosition)); */ - Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getDefaultEyePosition() const; float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } @@ -1020,6 +1029,9 @@ class MyAvatar : public Avatar { bool getAllowTeleporting() { return _allowTeleportingSetting.get(); } void setAllowTeleporting(bool allowTeleporting) { _allowTeleportingSetting.set(allowTeleporting); } + MyAvatar::TabletInputMode getTabletInputMode(); + void setTabletInputMode(MyAvatar::TabletInputMode mode); + bool getShowPlayArea() const { return _showPlayArea.get(); } void setShowPlayArea(bool showPlayArea) { _showPlayArea.set(showPlayArea); } @@ -1123,7 +1135,7 @@ class MyAvatar : public Avatar { * @example Report the current position of your avatar's head. * print(JSON.stringify(MyAvatar.getHeadPosition())); */ - Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getHeadPosition() const { return getHead()->getPosition(); } /*@jsdoc * Gets the yaw of the avatar's head relative to its body. @@ -1163,7 +1175,7 @@ class MyAvatar : public Avatar { * var eyePosition = MyAvatar.getEyePosition(); * print(JSON.stringify(eyePosition)); */ - Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getEyePosition() const { return getHead()->getEyePosition(); } /*@jsdoc * Gets the position of the avatar your avatar is currently looking at. @@ -1174,7 +1186,7 @@ class MyAvatar : public Avatar { */ // FIXME: If not looking at an avatar, the most recently looked-at position is returned. This should be fixed to return // undefined or {NaN, NaN, NaN} or similar. - Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getTargetAvatarPosition() const { return _targetAvatarPosition; } /*@jsdoc * Gets information on the avatar your avatar is currently looking at. @@ -1195,7 +1207,7 @@ class MyAvatar : public Avatar { * @example Report the position of your left hand relative to your avatar. * print(JSON.stringify(MyAvatar.getLeftHandPosition())); */ - Q_INVOKABLE glm::vec3 getLeftHandPosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getLeftHandPosition() const; /*@jsdoc * Gets the position of the avatar's right hand, relative to the avatar, as positioned by a hand controller (e.g., Oculus @@ -1208,7 +1220,7 @@ class MyAvatar : public Avatar { * @example Report the position of your right hand relative to your avatar. * print(JSON.stringify(MyAvatar.getLeftHandPosition())); */ - Q_INVOKABLE glm::vec3 getRightHandPosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getRightHandPosition() const; /*@jsdoc * Gets the position 0.3m in front of the left hand's position in the direction along the palm, in avatar coordinates, as @@ -1217,7 +1229,7 @@ class MyAvatar : public Avatar { * @returns {Vec3} The position 0.3m in front of the left hand's position in the direction along the palm, in avatar * coordinates. If the hand isn't being positioned by a controller, {@link Vec3(0)|Vec3.ZERO} is returned. */ - Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getLeftHandTipPosition() const; /*@jsdoc * Gets the position 0.3m in front of the right hand's position in the direction along the palm, in avatar coordinates, as @@ -1226,7 +1238,7 @@ class MyAvatar : public Avatar { * @returns {Vec3} The position 0.3m in front of the right hand's position in the direction along the palm, in avatar * coordinates. If the hand isn't being positioned by a controller, {@link Vec3(0)|Vec3.ZERO} is returned. */ - Q_INVOKABLE glm::vec3 getRightHandTipPosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getRightHandTipPosition() const; /*@jsdoc @@ -1285,15 +1297,15 @@ class MyAvatar : public Avatar { void snapOtherAvatarLookAtTargetsToMe(const AvatarHash& hash); void clearLookAtTargetAvatar(); - virtual void setJointRotations(const QVector& jointRotations) override; - virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override; - virtual void setJointRotation(int index, const glm::quat& rotation) override; - virtual void setJointTranslation(int index, const glm::vec3& translation) override; + virtual void setJointRotations(const QVector>& jointRotations) override; + virtual void setJointData(int index, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& translation) override; + virtual void setJointRotation(int index, const glm::qua& rotation) override; + virtual void setJointTranslation(int index, const glm::vec<3,float,glm::packed_highp>& translation) override; virtual void clearJointData(int index) override; - virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) override; - virtual void setJointRotation(const QString& name, const glm::quat& rotation) override; - virtual void setJointTranslation(const QString& name, const glm::vec3& translation) override; + virtual void setJointData(const QString& name, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& translation) override; + virtual void setJointRotation(const QString& name, const glm::qua& rotation) override; + virtual void setJointTranslation(const QString& name, const glm::vec<3,float,glm::packed_highp>& translation) override; virtual void clearJointData(const QString& name) override; virtual void clearJointsData() override; @@ -1306,7 +1318,7 @@ class MyAvatar : public Avatar { * @param {Quat} orientation - The orientation of the joint in world coordinates. * @returns {boolean} true if the joint was pinned, false if it wasn't. */ - Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation); + Q_INVOKABLE bool pinJoint(int index, const glm::vec<3,float,glm::packed_highp>& position, const glm::qua& orientation); bool isJointPinned(int index); @@ -1359,7 +1371,7 @@ class MyAvatar : public Avatar { void updateMotors(); void prepareForPhysicsSimulation(); - void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. + void nextAttitude(glm::vec<3,float,glm::packed_highp> position, glm::qua orientation); // Can be safely called at any time. void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } @@ -1393,10 +1405,10 @@ class MyAvatar : public Avatar { AudioListenerMode getAudioListenerMode() { return _audioListenerMode; } void setAudioListenerMode(AudioListenerMode audioListenerMode); - glm::vec3 getCustomListenPosition() { return _customListenPosition; } - void setCustomListenPosition(glm::vec3 customListenPosition) { _customListenPosition = customListenPosition; } - glm::quat getCustomListenOrientation() { return _customListenOrientation; } - void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } + glm::vec<3,float,glm::packed_highp> getCustomListenPosition() { return _customListenPosition; } + void setCustomListenPosition(glm::vec<3,float,glm::packed_highp> customListenPosition) { _customListenPosition = customListenPosition; } + glm::qua getCustomListenOrientation() { return _customListenOrientation; } + void setCustomListenOrientation(glm::qua customListenOrientation) { _customListenOrientation = customListenOrientation; } virtual void rebuildCollisionShape() override; @@ -1408,8 +1420,8 @@ class MyAvatar : public Avatar { void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; } float getCurrentStandingHeight() const { return _currentStandingHeight; } void setCurrentStandingHeight(float newMode) { _currentStandingHeight = newMode; } - const glm::quat getAverageHeadRotation() const { return _averageHeadRotation; } - void setAverageHeadRotation(glm::quat rotation) { _averageHeadRotation = rotation; } + const glm::qua getAverageHeadRotation() const { return _averageHeadRotation; } + void setAverageHeadRotation(glm::qua rotation) { _averageHeadRotation = rotation; } bool getResetMode() const { return _resetMode; } void setResetMode(bool hasBeenReset) { _resetMode = hasBeenReset; } @@ -1601,7 +1613,7 @@ class MyAvatar : public Avatar { * var headRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(headIndex); * print("Head rotation: " + JSON.stringify(Quat.safeEulerAngles(headRotation))); // Degrees */ - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + virtual glm::qua getAbsoluteJointRotationInObjectFrame(int index) const override; /*@jsdoc * Gets the translation of a joint relative to the avatar. @@ -1614,19 +1626,19 @@ class MyAvatar : public Avatar { * var headTranslation = MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex); * print("Head translation: " + JSON.stringify(headTranslation)); */ - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + virtual glm::vec<3,float,glm::packed_highp> getAbsoluteJointTranslationInObjectFrame(int index) const override; // all calibration matrices are in absolute sensor space. - glm::mat4 getCenterEyeCalibrationMat() const; - glm::mat4 getHeadCalibrationMat() const; - glm::mat4 getSpine2CalibrationMat() const; - glm::mat4 getHipsCalibrationMat() const; - glm::mat4 getLeftFootCalibrationMat() const; - glm::mat4 getRightFootCalibrationMat() const; - glm::mat4 getRightArmCalibrationMat() const; - glm::mat4 getLeftArmCalibrationMat() const; - glm::mat4 getLeftHandCalibrationMat() const; - glm::mat4 getRightHandCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getCenterEyeCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getHeadCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getSpine2CalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getHipsCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getLeftFootCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getRightFootCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getRightArmCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getLeftArmCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getLeftHandCalibrationMat() const; + glm::mat<4,4,float,glm::packed_highp> getRightHandCalibrationMat() const; void addHoldAction(AvatarActionHold* holdAction); // thread-safe void removeHoldAction(AvatarActionHold* holdAction); // thread-safe @@ -1635,16 +1647,16 @@ class MyAvatar : public Avatar { // derive avatar body position and orientation from the current HMD Sensor location. // results are in sensor frame (-z forward) - glm::mat4 deriveBodyFromHMDSensor(const bool forceFollowYPos = false) const; + glm::mat<4,4,float,glm::packed_highp> deriveBodyFromHMDSensor(const bool forceFollowYPos = false) const; - glm::mat4 getSpine2RotationRigSpace() const; + glm::mat<4,4,float,glm::packed_highp> getSpine2RotationRigSpace() const; - glm::vec3 computeCounterBalance(); + glm::vec<3,float,glm::packed_highp> computeCounterBalance(); // derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous // location of the base of support of the avatar. // results are in sensor frame (-z foward) - glm::mat4 deriveBodyUsingCgModel(); + glm::mat<4,4,float,glm::packed_highp> deriveBodyUsingCgModel(); /*@jsdoc * Tests whether a vector is pointing in the general direction of the avatar's "up" direction (i.e., dot product of vectors @@ -1654,7 +1666,7 @@ class MyAvatar : public Avatar { * @returns {boolean} true if the direction vector is pointing generally in the direction of the avatar's "up" * direction. */ - Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. + Q_INVOKABLE bool isUp(const glm::vec<3,float,glm::packed_highp>& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. /*@jsdoc * Tests whether a vector is pointing in the general direction of the avatar's "down" direction (i.e., dot product of @@ -1664,14 +1676,14 @@ class MyAvatar : public Avatar { * @returns {boolean} true if the direction vector is pointing generally in the direction of the avatar's * "down" direction. */ - Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; + Q_INVOKABLE bool isDown(const glm::vec<3,float,glm::packed_highp>& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; void setUserHeight(float value); float getUserHeight() const; float getUserEyeHeight() const; virtual SpatialParentTree* getParentTree() const override; - virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getSensorToWorldScale()); } + virtual glm::vec<3,float,glm::packed_highp> scaleForChildren() const override { return glm::vec3(getSensorToWorldScale()); } const QUuid& getSelfID() const { return AVATAR_SELF_ID; } @@ -1702,9 +1714,9 @@ class MyAvatar : public Avatar { bool isReadyForPhysics() const; float computeStandingHeightMode(const controller::Pose& head); - glm::quat computeAverageHeadRotation(const controller::Pose& head); + glm::qua computeAverageHeadRotation(const controller::Pose& head); - glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } + glm::vec<3,float,glm::packed_highp> getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } void prepareAvatarEntityDataForReload(); /*@jsdoc @@ -1714,14 +1726,14 @@ class MyAvatar : public Avatar { * @function MyAvatar.setHeadLookAt * @param {Vec3} lookAtTarget - The target point in world coordinates. */ - Q_INVOKABLE void setHeadLookAt(const glm::vec3& lookAtTarget); + Q_INVOKABLE void setHeadLookAt(const glm::vec<3,float,glm::packed_highp>& lookAtTarget); /*@jsdoc * Gets the current target point of the head's look direction in world coordinates. * @function MyAvatar.getHeadLookAt * @returns {Vec3} The head's look-at target in world coordinates. */ - Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getHeadLookAt() { return _lookAtCameraTarget; } /*@jsdoc * Returns control of the avatar's head to the engine, and releases control from API calls. @@ -1736,14 +1748,14 @@ class MyAvatar : public Avatar { * @function MyAvatar.setEyesLookAt * @param {Vec3} lookAtTarget - The target point in world coordinates. */ - Q_INVOKABLE void setEyesLookAt(const glm::vec3& lookAtTarget); + Q_INVOKABLE void setEyesLookAt(const glm::vec<3,float,glm::packed_highp>& lookAtTarget); /*@jsdoc * Gets the current target point of the eyes look direction in world coordinates. * @function MyAvatar.getEyesLookAt * @returns {Vec3} The eyes' look-at target in world coordinates. */ - Q_INVOKABLE glm::vec3 getEyesLookAt() { return _eyesLookAtTarget.get(); } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getEyesLookAt() { return _eyesLookAtTarget.get(); } /*@jsdoc * Returns control of the avatar's eyes to the engine, and releases control from API calls. @@ -1759,9 +1771,9 @@ class MyAvatar : public Avatar { * @param {Vec3} pointAtTarget - The target to point at, in world coordinates. * @returns {boolean} true if the target point was set, false if it wasn't. */ - Q_INVOKABLE bool setPointAt(const glm::vec3& pointAtTarget); + Q_INVOKABLE bool setPointAt(const glm::vec<3,float,glm::packed_highp>& pointAtTarget); - glm::quat getLookAtRotation() { return _lookAtYaw * _lookAtPitch; } + glm::qua getLookAtRotation() { return _lookAtYaw * _lookAtPitch; } /*@jsdoc * Creates a new grab that grabs an entity. @@ -1791,7 +1803,7 @@ class MyAvatar : public Avatar { * }, 10000); */ Q_INVOKABLE const QUuid grab(const QUuid& targetID, int parentJointIndex, - glm::vec3 positionalOffset, glm::quat rotationalOffset); + glm::vec<3,float,glm::packed_highp> positionalOffset, glm::qua rotationalOffset); /*@jsdoc * Releases (deletes) a grab to stop grabbing an entity. @@ -1871,7 +1883,7 @@ class MyAvatar : public Avatar { * @param {Vec3} position - The position where the avatar should sit. * @param {Quat} rotation - The initial orientation of the seated avatar. */ - Q_INVOKABLE void beginSit(const glm::vec3& position, const glm::quat& rotation); + Q_INVOKABLE void beginSit(const glm::vec<3,float,glm::packed_highp>& position, const glm::qua& rotation); /*@jsdoc * Ends a sitting action for the avatar. @@ -1879,7 +1891,7 @@ class MyAvatar : public Avatar { * @param {Vec3} position - The position of the avatar when standing up. * @param {Quat} rotation - The orientation of the avatar when standing up. */ - Q_INVOKABLE void endSit(const glm::vec3& position, const glm::quat& rotation); + Q_INVOKABLE void endSit(const glm::vec<3,float,glm::packed_highp>& position, const glm::qua& rotation); /*@jsdoc * Gets whether the avatar is in a seated pose. The seated pose is set by calling {@link MyAvatar.beginSit}. @@ -1904,8 +1916,8 @@ class MyAvatar : public Avatar { void debugDrawPose(controller::Action action, const char* channelName, float size); bool getIsJointOverridden(int jointIndex) const; - glm::vec3 getLookAtPivotPoint(); - glm::vec3 getCameraEyesPosition(float deltaTime); + glm::vec<3,float,glm::packed_highp> getLookAtPivotPoint(); + glm::vec<3,float,glm::packed_highp> getCameraEyesPosition(float deltaTime); bool isJumping(); bool getHMDCrouchRecenterEnabled() const; bool isAllowedToLean() const; @@ -1982,8 +1994,8 @@ public slots: * @param {boolean} [shouldFaceLocation=false] - Set to true to position the avatar a short distance away from * the new position and orientate the avatar to face the position. */ - void goToFeetLocation(const glm::vec3& newPosition, bool hasOrientation = false, - const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); + void goToFeetLocation(const glm::vec<3,float,glm::packed_highp>& newPosition, bool hasOrientation = false, + const glm::qua& newOrientation = glm::qua(), bool shouldFaceLocation = false); /*@jsdoc * Moves the avatar to a new position and/or orientation in the domain. @@ -1995,8 +2007,8 @@ public slots: * the new position and orientate the avatar to face the position. * @param {boolean} [withSafeLanding=true] - Set to false to disable safe landing when teleporting. */ - void goToLocation(const glm::vec3& newPosition, - bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), + void goToLocation(const glm::vec<3,float,glm::packed_highp>& newPosition, + bool hasOrientation = false, const glm::qua& newOrientation = glm::qua(), bool shouldFaceLocation = false, bool withSafeLanding = true); /*@jsdoc * Moves the avatar to a new position and (optional) orientation in the domain, with safe landing. @@ -2010,7 +2022,7 @@ public slots: * @function MyAvatar.goToLocationAndEnableCollisions * @param {Vec3} position - The new position for the avatar, in world coordinates. */ - void goToLocationAndEnableCollisions(const glm::vec3& newPosition); + void goToLocationAndEnableCollisions(const glm::vec<3,float,glm::packed_highp>& newPosition); /*@jsdoc * @function MyAvatar.safeLanding @@ -2018,7 +2030,7 @@ public slots: * @returns {boolean} true if the avatar was moved, false if it wasn't. * @deprecated This function is deprecated and will be removed. */ - bool safeLanding(const glm::vec3& position); + bool safeLanding(const glm::vec<3,float,glm::packed_highp>& position); /*@jsdoc @@ -2043,7 +2055,7 @@ public slots: * properties instead. */ // Set/Get update the thrust that will move the avatar around - void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; + void addThrust(glm::vec<3,float,glm::packed_highp> newThrust) { _thrust += newThrust; }; /*@jsdoc * Gets the thrust currently being applied to your avatar. @@ -2052,7 +2064,7 @@ public slots: * @deprecated This function is deprecated and will be removed. Use {@link MyAvatar|MyAvatar.motorVelocity} and related * properties instead. */ - glm::vec3 getThrust() { return _thrust; }; + glm::vec<3,float,glm::packed_highp> getThrust() { return _thrust; }; /*@jsdoc * Sets the thrust to be applied to your avatar for a short while. @@ -2061,7 +2073,7 @@ public slots: * @deprecated This function is deprecated and will be removed. Use {@link MyAvatar|MyAvatar.motorVelocity} and related * properties instead. */ - void setThrust(glm::vec3 newThrust) { _thrust = newThrust; } + void setThrust(glm::vec<3,float,glm::packed_highp> newThrust) { _thrust = newThrust; } /*@jsdoc @@ -2237,7 +2249,7 @@ public slots: * @function MyAvatar.getPositionForAudio * @returns {Vec3} Your listening position. */ - glm::vec3 getPositionForAudio(); + glm::vec<3,float,glm::packed_highp> getPositionForAudio(); /*@jsdoc * Gets the orientation of your listening position for spatialized audio. The orientation depends on the value of the @@ -2245,7 +2257,7 @@ public slots: * @function MyAvatar.getOrientationForAudio * @returns {Quat} The orientation of your listening position. */ - glm::quat getOrientationForAudio(); + glm::qua getOrientationForAudio(); /*@jsdoc * @function MyAvatar.setModelScale @@ -2499,6 +2511,14 @@ public slots: */ void disableHandTouchForIDChanged(const QUuid& entityID, bool disable); + /*@jsdoc + * Triggered when {@link MyAvatar.tabletInputMode} is changed. + * @function MyAvatar.tabletInputModeChanged + * @param {MyAvatar.TabletInputMode} + * @returns {Signal} + */ + void tabletInputModeChanged(MyAvatar::TabletInputMode mode); + private slots: void leaveDomain(); void updateCollisionCapsuleCache(); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 65c3d828caf..79cbf2091fa 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -138,6 +138,8 @@ int main(int argc, const char* argv[]) { qputenv("QSG_DISTANCEFIELD_ANTIALIASING", "gray"); } + QSurfaceFormat::setDefaultFormat(getDefaultOpenGLSurfaceFormat()); + // Setup QCoreApplication settings, install log message handler setupHifiApplication(BuildInfo::INTERFACE_NAME); @@ -771,15 +773,15 @@ int main(int argc, const char* argv[]) { if (parser.isSet(checkMinSpecOption)) { QString appPath; { - char filename[MAX_PATH]; + wchar_t filename[MAX_PATH]; GetModuleFileName(NULL, filename, MAX_PATH); - QFileInfo appInfo(filename); + QFileInfo appInfo(QString::fromWCharArray(filename)); appPath = appInfo.absolutePath(); } QString openvrDllPath = appPath + "/plugins/openvr.dll"; HMODULE openvrDll; CHECKMINSPECPROC checkMinSpecPtr; - if ((openvrDll = LoadLibrary(openvrDllPath.toLocal8Bit().data())) && + if ((openvrDll = LoadLibrary(qUtf16Printable(openvrDllPath.toLocal8Bit().data()))) && (checkMinSpecPtr = (CHECKMINSPECPROC)GetProcAddress(openvrDll, "CheckMinSpec"))) { if (!checkMinSpecPtr()) { return -1; @@ -852,8 +854,11 @@ int main(int argc, const char* argv[]) { } QTranslator translator; - translator.load("i18n/interface_en"); - app.installTranslator(&translator); + if (translator.load("i18n/interface_en")) { + app.installTranslator(&translator); + } else { + qWarning() << " Failed to load QTranslator i18n/interface_en"; + } qCDebug(interfaceapp, "Created QT Application."); if (parser.isSet("abortAfterInit")) { return 99; diff --git a/interface/src/networking/CloseEventSender.h b/interface/src/networking/CloseEventSender.h index b74412c41c0..4be84c1f792 100644 --- a/interface/src/networking/CloseEventSender.h +++ b/interface/src/networking/CloseEventSender.h @@ -16,6 +16,7 @@ #include #include +#include #include diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index b0f21e2574c..9cb68512dd7 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -26,6 +26,7 @@ OctreePacketProcessor::OctreePacketProcessor(): { setObjectName("Octree Packet Processor"); + Q_ASSERT(DependencyManager::get()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); const PacketReceiver::PacketTypeList octreePackets = { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete }; diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index f652639d20e..e71a5274d82 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -134,7 +134,7 @@ bool CollisionPick::getShapeInfoReady(const CollisionRegion& pick) { return _mathPick.loaded; } -void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { +void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, std::shared_ptr resource) { ShapeType type = shapeInfo.getType(); glm::vec3 dimensions = pick.transform.getScale(); QString modelURL = (resource ? resource->getURL().toString() : ""); @@ -147,7 +147,7 @@ void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, } } -void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { +void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, std::shared_ptr resource) { // This code was copied and modified from RenderableModelEntityItem::computeShapeInfo // TODO: Move to some shared code area (in entities-renderer? model-networking?) // after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo @@ -305,7 +305,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha points.reserve(sizeToReserve); // copy points - const glm::vec3* vertexItr = vertices.cbegin(); + auto vertexItr = vertices.cbegin(); while (vertexItr != vertices.cend()) { glm::vec3 point = *vertexItr; points.push_back(point); @@ -322,7 +322,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha triangleIndices.reserve((int)triangleIndicesCount); for (const HFMMeshPart& meshPart : mesh.parts) { - const int* indexItr = meshPart.triangleIndices.cbegin(); + auto indexItr = meshPart.triangleIndices.cbegin(); while (indexItr != meshPart.triangleIndices.cend()) { triangleIndices.push_back(*indexItr); ++indexItr; diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 8825ea65d11..31a1a3fd688 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -65,14 +65,14 @@ class CollisionPick : public Pick { bool isLoaded() const; // Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use. bool getShapeInfoReady(const CollisionRegion& pick); - void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); - void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); + void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, std::shared_ptr resource); + void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, std::shared_ptr resource); void filterIntersections(std::vector& intersections) const; bool _scaleWithParent; PhysicsEnginePointer _physicsEngine; - QSharedPointer _cachedResource; + std::shared_ptr _cachedResource; // Options for what information to get from collision results bool _includeNormals; diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 508745ca32d..94a24295922 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -49,7 +49,7 @@ unsigned int PointerScriptingInterface::createPointerInternal(const PickQuery::P // Interaction with managers should always happen on the main thread if (QThread::currentThread() != qApp->thread()) { unsigned int result; - BLOCKING_INVOKE_METHOD(this, "createPointerInternal", Q_RETURN_ARG(unsigned int, result), Q_ARG(PickQuery::PickType, type), Q_ARG(PointerProperties, properties)); + BLOCKING_INVOKE_METHOD(this, "createPointerInternal", Q_GENERIC_RETURN_ARG(unsigned int, result), Q_GENERIC_ARG(PickQuery::PickType, type), Q_GENERIC_ARG(PointerProperties, properties)); return result; } diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index b45bd46dcff..38d686bb91e 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -421,7 +421,7 @@ class PointerScriptingInterface : public QObject, public Dependency { * @param {Mat4} [offset] - The offset of the target point from the center of the target item. If not specified, the * pointer locks on to the center of the target item. */ - Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat<4,4,float,glm::packed_highp>& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } /*@jsdoc * Sets the delay of a Ray pointer. diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 13954cf2972..8acb47a1333 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -167,13 +167,12 @@ void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createGetAllMappingsRequest(); - connect(request, &GetAllMappingsRequest::finished, this, [callback](GetAllMappingsRequest* request) mutable { + connect(request, &GetAllMappingsRequest::finished, this, [callback, this](GetAllMappingsRequest* request) mutable { auto mappings = request->getMappings(); - OVERTE_IGNORE_DEPRECATED_BEGIN - // Still using QScriptEngine - auto map = callback.engine()->newObject(); - OVERTE_IGNORE_WARNING_END + // QT6TODO: is this correct? + auto engine = qjsEngine(this); + auto map = engine->newObject(); for (auto& kv : mappings ) { map.setProperty(kv.first, kv.second.hash); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index bc624b0dd08..921b393ad49 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -78,7 +78,9 @@ class AssetMappingsScriptingInterface : public QObject, public Dependency { Q_INVOKABLE void getMapping(QString path, QJSValue callback = QJSValue()); Q_INVOKABLE void uploadFile(QString path, QString mapping, QJSValue startedCallback = QJSValue(), QJSValue completedCallback = QJSValue(), bool dropEvent = false); Q_INVOKABLE void deleteMappings(QStringList paths, QJSValue callback = QJSValue()); - Q_INVOKABLE void deleteMapping(QString path, QJSValue callback) { deleteMappings(QStringList(path), callback = QJSValue()); } + Q_INVOKABLE void deleteMapping(QString path, QJSValue callback = QJSValue()) { + deleteMappings(QStringList(path), callback); + } Q_INVOKABLE void getAllMappings(QJSValue callback = QJSValue()); Q_INVOKABLE void renameMapping(QString oldPath, QString newPath, QJSValue callback = QJSValue()); Q_INVOKABLE void setBakingEnabled(QStringList paths, bool enabled, QJSValue callback = QJSValue()); diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index f90a7d84a8b..9255b4b1bc1 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -33,8 +33,8 @@ static Setting::Handle hmdOutputDeviceSetting { QStringList { OVERTE_AU Q_DECLARE_METATYPE(HifiAudioDeviceInfo); -Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { - if (mode == QAudio::AudioInput) { +Setting::Handle& getSetting(bool contextIsHMD, QAudioDevice::Mode mode) { + if (mode == QAudioDevice::Mode::Input) { return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; } else { // if (mode == QAudio::AudioOutput) return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting; @@ -59,7 +59,7 @@ QHash AudioDeviceList::_roles { { TypeRole, "type"} }; -static QString getTargetDevice(bool hmd, QAudio::Mode mode) { +static QString getTargetDevice(bool hmd, QAudioDevice::Mode mode) { QString deviceName; auto& setting = getSetting(hmd, mode); if (setting.isSet()) { @@ -70,11 +70,11 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) { return deviceName; } -static void checkHmdDefaultsChange(QAudio::Mode mode) { +static void checkHmdDefaultsChange(QAudioDevice::Mode mode) { QString name; foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getAllDisplayPlugins()) { if (displayPlugin && displayPlugin->isHmd()) { - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { name = displayPlugin->getPreferredAudioInDevice(); } else { name = displayPlugin->getPreferredAudioOutDevice(); @@ -86,16 +86,16 @@ static void checkHmdDefaultsChange(QAudio::Mode mode) { if (!name.isEmpty()) { auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setHmdAudioName", - Q_ARG(QAudio::Mode, mode), + Q_ARG(QAudioDevice::Mode, mode), Q_ARG(const QString&, name)); } } Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; -AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { - if (mode == QAudio::AudioInput) { - auto& setting1 = getSetting(true, QAudio::AudioInput); +AudioDeviceList::AudioDeviceList(QAudioDevice::Mode mode) : _mode(mode) { + if (mode == QAudioDevice::Mode::Input) { + auto& setting1 = getSetting(true, QAudioDevice::Mode::Input); if (setting1.isSet()) { qDebug() << "Device name in settings for HMD, Input" << setting1.get(); _backupSelectedHMDDeviceName = setting1.get(); @@ -104,8 +104,8 @@ AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { } } - if (mode == QAudio::AudioOutput) { - auto& setting2 = getSetting(true, QAudio::AudioOutput); + if (mode == QAudioDevice::Mode::Output) { + auto& setting2 = getSetting(true, QAudioDevice::Mode::Output); if (setting2.isSet()) { qDebug() << "Device name in settings for HMD, Output" << setting2.get(); _backupSelectedHMDDeviceName = setting2.get(); @@ -114,8 +114,8 @@ AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { } } - if (mode == QAudio::AudioInput) { - auto& setting3 = getSetting(false, QAudio::AudioInput); + if (mode == QAudioDevice::Mode::Input) { + auto& setting3 = getSetting(false, QAudioDevice::Mode::Input); if (setting3.isSet()) { qDebug() << "Device name in settings for Desktop, Input" << setting3.get(); _backupSelectedDesktopDeviceName = setting3.get(); @@ -124,8 +124,8 @@ AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { } } - if (mode == QAudio::AudioOutput) { - auto& setting4 = getSetting(false, QAudio::AudioOutput); + if (mode == QAudioDevice::Mode::Output) { + auto& setting4 = getSetting(false, QAudioDevice::Mode::Output); if (setting4.isSet()) { qDebug() << "Device name in settings for Desktop, Output" << setting4.get(); _backupSelectedDesktopDeviceName = setting4.get(); @@ -188,13 +188,13 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { // FIXME can't use blocking connections here, so we can't determine whether the switch succeeded or not // We need to have the AudioClient emit signals on switch success / failure QMetaObject::invokeMethod(client, "switchAudioDevice", - Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName), Q_ARG(bool, contextIsHMD)); + Q_ARG(QAudioDevice::Mode, _mode), Q_ARG(QString, deviceName), Q_ARG(bool, contextIsHMD)); #if 0 bool switchResult = false; QMetaObject::invokeMethod(client, "switchAudioDevice", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, switchResult), - Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); + Q_ARG(QAudioDevice::Mode, _mode), Q_ARG(QString, deviceName)); // try to set to the default device for this mode if (!switchResult) { @@ -206,11 +206,11 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); } if (!deviceName.isNull()) { - QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudioDevice::Mode, _mode), Q_ARG(QString, deviceName)); } } else { // use the system default - QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode)); + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudioDevice::Mode, _mode)); } } #endif @@ -280,7 +280,7 @@ std::shared_ptr getSimilarDevice(const QString& deviceNa } -void AudioDeviceList::onDevicesChanged(QAudio::Mode mode, const QList& devices) { +void AudioDeviceList::onDevicesChanged(QAudioDevice::Mode mode, const QList& devices) { beginResetModel(); QList> newDevices; @@ -311,19 +311,7 @@ void AudioDeviceList::onDevicesChanged(QAudio::Mode mode, const QList& peakValueList) { - assert(_mode == QAudio::AudioInput); + assert(_mode == QAudioDevice::Mode::Input); if (peakValueList.length() != rowCount()) { qWarning() << "AudioDeviceList" << __FUNCTION__ << "length mismatch"; @@ -434,8 +422,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { connect(client, &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); connect(client, &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection); - checkHmdDefaultsChange(QAudio::AudioInput); - checkHmdDefaultsChange(QAudio::AudioOutput); + checkHmdDefaultsChange(QAudioDevice::Mode::Input); + checkHmdDefaultsChange(QAudioDevice::Mode::Output); } AudioDevices::~AudioDevices() {} @@ -445,7 +433,7 @@ void AudioDevices::onContextChanged(const QString& context) { _outputs.resetDevice(_contextIsHMD); } -void AudioDevices::onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo& device, +void AudioDevices::onDeviceSelected(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device, const HifiAudioDeviceInfo& previousDevice, bool isHMD) { QString deviceName = device.deviceName(); @@ -463,7 +451,7 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo const QString MODE = "audio_mode"; const QString INPUT = "INPUT"; - const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; + const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudioDevice::Mode::Input ? INPUT : OUTPUT; const QString CONTEXT = "display_mode"; data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP; @@ -482,10 +470,10 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo } } -void AudioDevices::onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device) { - if (mode == QAudio::AudioInput) { +void AudioDevices::onDeviceChanged(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device) { + if (mode == QAudioDevice::Mode::Input) { if (_requestedInputDevice == device) { - onDeviceSelected(QAudio::AudioInput, device, + onDeviceSelected(QAudioDevice::Mode::Input, device, _contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, _contextIsHMD); _requestedInputDevice = HifiAudioDeviceInfo(); @@ -493,7 +481,7 @@ void AudioDevices::onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& _inputs.onDeviceChanged(device, _contextIsHMD); } else { // if (mode == QAudio::AudioOutput) if (_requestedOutputDevice == device) { - onDeviceSelected(QAudio::AudioOutput, device, + onDeviceSelected(QAudioDevice::Mode::Output, device, _contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, _contextIsHMD); _requestedOutputDevice = HifiAudioDeviceInfo(); @@ -502,21 +490,21 @@ void AudioDevices::onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& } } -void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { +void AudioDevices::onDevicesChanged(QAudioDevice::Mode mode, const QList& devices) { static std::once_flag once; std::call_once(once, [&] { //readout settings - _inputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioInput); - _inputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioInput); + _inputs._hmdSavedDeviceName = getTargetDevice(true, QAudioDevice::Mode::Input); + _inputs._desktopSavedDeviceName = getTargetDevice(false, QAudioDevice::Mode::Input); - _outputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioOutput); - _outputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioOutput); + _outputs._hmdSavedDeviceName = getTargetDevice(true, QAudioDevice::Mode::Output); + _outputs._desktopSavedDeviceName = getTargetDevice(false, QAudioDevice::Mode::Output); onContextChanged(QString()); }); //set devices for both contexts - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { _inputs.onDevicesChanged(mode, devices); static std::once_flag onceAfterInputDevicesChanged; @@ -540,11 +528,11 @@ void AudioDevices::chooseInputDevice(const HifiAudioDeviceInfo& device, bool isH auto client = DependencyManager::get().data(); _requestedInputDevice = device; QMetaObject::invokeMethod(client, "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(QAudioDevice::Mode, QAudioDevice::Mode::Input), Q_ARG(const HifiAudioDeviceInfo&, device)); } else { //context is different. just save device in settings - onDeviceSelected(QAudio::AudioInput, device, + onDeviceSelected(QAudioDevice::Mode::Input, device, isHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, isHMD); _inputs.onDeviceChanged(device, isHMD); @@ -557,11 +545,11 @@ void AudioDevices::chooseOutputDevice(const HifiAudioDeviceInfo& device, bool is auto client = DependencyManager::get().data(); _requestedOutputDevice = device; QMetaObject::invokeMethod(client, "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioOutput), + Q_ARG(QAudioDevice::Mode, QAudioDevice::Mode::Output), Q_ARG(const HifiAudioDeviceInfo&, device)); } else { //context is different. just save device in settings - onDeviceSelected(QAudio::AudioOutput, device, + onDeviceSelected(QAudioDevice::Mode::Output, device, isHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, isHMD); _outputs.onDeviceChanged(device, isHMD); diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 24d7580d0d6..8e61cb7c7b2 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -17,9 +17,9 @@ #include #include -#include +#include -#include +#include <../../audio-client/src/HifiAudioDeviceInfo.h> namespace scripting { @@ -36,7 +36,7 @@ class AudioDeviceList : public QAbstractListModel { Q_OBJECT public: - AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput); + AudioDeviceList(QAudioDevice::Mode mode = QAudioDevice::Mode::Output); virtual ~AudioDeviceList(); virtual std::shared_ptr newDevice(const AudioDevice& device) @@ -58,14 +58,14 @@ class AudioDeviceList : public QAbstractListModel { protected slots: void onDeviceChanged(const HifiAudioDeviceInfo& device, bool isHMD); - void onDevicesChanged(QAudio::Mode mode, const QList& devices); + void onDevicesChanged(QAudioDevice::Mode mode, const QList& devices); protected: friend class AudioDevices; static QHash _roles; static Qt::ItemFlags _flags; - const QAudio::Mode _mode; + const QAudioDevice::Mode _mode; HifiAudioDeviceInfo _selectedDesktopDevice; HifiAudioDeviceInfo _selectedHMDDevice; QString _backupSelectedDesktopDeviceName; @@ -87,7 +87,7 @@ class AudioInputDeviceList : public AudioDeviceList { Q_PROPERTY(bool peakValuesEnabled READ peakValuesEnabled WRITE setPeakValuesEnabled NOTIFY peakValuesEnabledChanged) public: - AudioInputDeviceList() : AudioDeviceList(QAudio::AudioInput) {} + AudioInputDeviceList() : AudioDeviceList(QAudioDevice::Input) {} virtual ~AudioInputDeviceList() = default; virtual std::shared_ptr newDevice(const AudioDevice& device) override @@ -131,10 +131,10 @@ private slots: void chooseOutputDevice(const HifiAudioDeviceInfo& device, bool isHMD); void onContextChanged(const QString& context); - void onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo& device, + void onDeviceSelected(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device, const HifiAudioDeviceInfo& previousDevice, bool isHMD); - void onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device); - void onDevicesChanged(QAudio::Mode mode, const QList& devices); + void onDeviceChanged(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device); + void onDevicesChanged(QAudioDevice::Mode mode, const QList& devices); private: friend class Audio; @@ -143,7 +143,7 @@ private slots: AudioDeviceList* getOutputList() { return &_outputs; } AudioInputDeviceList _inputs; - AudioDeviceList _outputs { QAudio::AudioOutput }; + AudioDeviceList _outputs { QAudioDevice::Mode::Output }; HifiAudioDeviceInfo _requestedOutputDevice; HifiAudioDeviceInfo _requestedInputDevice; diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index 31a692afd72..69e839054c5 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -28,24 +28,24 @@ float ClipboardScriptingInterface::getClipboardContentsLargestDimension() { bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector& entityIDs, const QVariantMap& options) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "exportEntities", - Q_RETURN_ARG(bool, retVal), - Q_ARG(const QString&, filename), - Q_ARG(const QVector&, entityIDs), - Q_ARG(const glm::vec3*, nullptr), - Q_ARG(const QVariantMap&, options)); + Q_GENERIC_RETURN_ARG(bool, retVal), + Q_GENERIC_ARG(const QString&, filename), + Q_GENERIC_ARG(const QVector&, entityIDs), + Q_GENERIC_ARG(const glm::vec3*, nullptr), + Q_GENERIC_ARG(const QVariantMap&, options)); return retVal; } bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float scale, const QVariantMap& options) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "exportEntities", - Q_RETURN_ARG(bool, retVal), - Q_ARG(const QString&, filename), - Q_ARG(float, x), - Q_ARG(float, y), - Q_ARG(float, z), - Q_ARG(float, scale), - Q_ARG(const QVariantMap&, options)); + Q_GENERIC_RETURN_ARG(bool, retVal), + Q_GENERIC_ARG(const QString&, filename), + Q_GENERIC_ARG(float, x), + Q_GENERIC_ARG(float, y), + Q_GENERIC_ARG(float, z), + Q_GENERIC_ARG(float, scale), + Q_GENERIC_ARG(const QVariantMap&, options)); return retVal; } @@ -56,20 +56,20 @@ bool ClipboardScriptingInterface::importEntities( ) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "importEntities", - Q_RETURN_ARG(bool, retVal), - Q_ARG(const QString&, filename), - Q_ARG(const bool, isObservable), - Q_ARG(const qint64, callerId)); + Q_GENERIC_RETURN_ARG(bool, retVal), + Q_GENERIC_ARG(const QString&, filename), + Q_GENERIC_ARG(const bool, isObservable), + Q_GENERIC_ARG(const qint64, callerId)); return retVal; } QVector ClipboardScriptingInterface::pasteEntities(glm::vec3 position, const QString& entityHostType) { QVector retVal; BLOCKING_INVOKE_METHOD(qApp, "pasteEntities", - Q_RETURN_ARG(QVector, retVal), - Q_ARG(const QString&, entityHostType), - Q_ARG(float, position.x), - Q_ARG(float, position.y), - Q_ARG(float, position.z)); + Q_GENERIC_RETURN_ARG(QVector, retVal), + Q_GENERIC_ARG(const QString&, entityHostType), + Q_GENERIC_ARG(float, position.x), + Q_GENERIC_ARG(float, position.y), + Q_GENERIC_ARG(float, position.z)); return retVal; } diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 5310b46e4bf..8bd34c326a7 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -56,7 +56,7 @@ class ClipboardScriptingInterface : public QObject { * } * } */ - Q_INVOKABLE glm::vec3 getContentsDimensions(); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getContentsDimensions(); /*@jsdoc * Gets the largest dimension of the extents of the entities held in the clipboard. @@ -135,7 +135,7 @@ class ClipboardScriptingInterface : public QObject { * @returns {Uuid[]} The IDs of the new entities that were created as a result of the paste operation. If entities couldn't * be created then an empty array is returned. */ - Q_INVOKABLE QVector pasteEntities(glm::vec3 position, const QString& entityHostType = "domain"); + Q_INVOKABLE QVector pasteEntities(glm::vec<3,float,glm::packed_highp> position, const QString& entityHostType = "domain"); }; #endif // hifi_ClipboardScriptingInterface_h diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 4128976fdc5..ef6bddf1c67 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -345,7 +345,7 @@ public slots: * @function Controller.getViewportDimensions * @returns {Vec2} The dimensions of the Interface window interior if in desktop mode or HUD surface if in HMD mode. */ - virtual glm::vec2 getViewportDimensions() const; + virtual glm::vec<2,float,glm::packed_highp> getViewportDimensions() const; /*@jsdoc * Gets the recommended area to position UI on the HUD surface if in HMD mode or Interface's window interior if in desktop @@ -484,6 +484,8 @@ public slots: using InputKey = controller::InputController::Key; }; +Q_DECLARE_METATYPE(ControllerScriptingInterface); + const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip const int NUMBER_OF_JOYSTICKS_PER_PALM = 1; const int NUMBER_OF_TRIGGERS_PER_PALM = 1; diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index f7ac8f43c91..1dbe4e9a76e 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -116,12 +116,22 @@ void DesktopScriptingInterface::show(const QString& path, const QString& title) InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString& sourceUrl, const QVariantMap& properties) { if (QThread::currentThread() != thread()) { + qDebug() << "DesktopScriptingInterface::createWindow" << sourceUrl; InteractiveWindowPointer interactiveWindow = nullptr; - BLOCKING_INVOKE_METHOD(this, "createWindowOnThread", + // QT6TODO + /*BLOCKING_INVOKE_METHOD(this, "createWindowOnThread", + Q_GENERIC_RETURN_ARG(InteractiveWindowPointer, interactiveWindow), + Q_GENERIC_ARG(QString, sourceUrl), + Q_GENERIC_ARG(QVariantMap, properties), + Q_GENERIC_ARG(QThread*, QThread::currentThread()));*/ + QMetaObject::invokeMethod(this, &DesktopScriptingInterface::createWindowOnThread/*"createWindowOnThread"*/, + Qt::BlockingQueuedConnection, Q_RETURN_ARG(InteractiveWindowPointer, interactiveWindow), - Q_ARG(QString, sourceUrl), - Q_ARG(QVariantMap, properties), - Q_ARG(QThread*, QThread::currentThread())); + sourceUrl, + properties, + QThread::currentThread() + ); + return interactiveWindow; } @@ -141,6 +151,8 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const Q // The offscreen surface already validates against non-local QML sources, but we also need to ensure that // if we create top level QML, like dock widgets or other types of QQuickView containing desktop windows // that the source URL is permitted + // QT6TODO: crashes for some reason, temporarily disabled + qDebug() << "DesktopScriptingInterface::createWindowOnThread " << sourceUrl; const auto& urlValidator = OffscreenQmlSurface::getUrlValidator(); if (!urlValidator(sourceUrl)) { return nullptr; diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 81516ae441b..5e8e597ed4c 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -90,8 +90,8 @@ class ScriptEngine; */ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition) - Q_PROPERTY(glm::quat orientation READ getOrientation) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> position READ getPosition) + Q_PROPERTY(glm::qua orientation READ getOrientation) Q_PROPERTY(bool showTablet READ getShouldShowTablet) Q_PROPERTY(bool tabletContextualMode READ getTabletContextualMode) Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID) @@ -103,7 +103,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(int miniTabletHand READ getCurrentMiniTabletHand WRITE setCurrentMiniTabletHand) Q_PROPERTY(bool miniTabletEnabled READ getMiniTabletEnabled WRITE setMiniTabletEnabled) Q_PROPERTY(QVariant playArea READ getPlayAreaRect); - Q_PROPERTY(QVector sensorPositions READ getSensorPositions); + Q_PROPERTY(QVector> sensorPositions READ getSensorPositions); Q_PROPERTY(float visionSqueezeRatioX READ getVisionSqueezeRatioX WRITE setVisionSqueezeRatioX); Q_PROPERTY(float visionSqueezeRatioY READ getVisionSqueezeRatioY WRITE setVisionSqueezeRatioY); @@ -142,9 +142,9 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * Overlays.deleteOverlay(square); * }); */ - Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> calculateRayUICollisionPoint(const glm::vec<3,float,glm::packed_highp>& position, const glm::vec<3,float,glm::packed_highp>& direction) const; - glm::vec3 calculateParabolaUICollisionPoint(const glm::vec3& position, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance) const; + glm::vec<3,float,glm::packed_highp> calculateParabolaUICollisionPoint(const glm::vec<3,float,glm::packed_highp>& position, const glm::vec<3,float,glm::packed_highp>& velocity, const glm::vec<3,float,glm::packed_highp>& acceleration, float& parabolicDistance) const; /*@jsdoc * Gets the 2D HUD overlay coordinates of a 3D point on the HUD overlay. @@ -170,7 +170,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * Overlays.deleteOverlay(square); * }); */ - Q_INVOKABLE glm::vec2 overlayFromWorldPoint(const glm::vec3& position) const; + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> overlayFromWorldPoint(const glm::vec<3,float,glm::packed_highp>& position) const; /*@jsdoc * Gets the 3D world coordinates of a 2D point on the HUD overlay. @@ -179,7 +179,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * @param {Vec2} coordinates - The point on the HUD overlay in HUD coordinates. * @returns {Vec3} The point on the HUD overlay in world coordinates. */ - Q_INVOKABLE glm::vec3 worldPointFromOverlay(const glm::vec2& overlay) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldPointFromOverlay(const glm::vec<2,float,glm::packed_highp>& overlay) const; /*@jsdoc * Gets the 2D point on the HUD overlay represented by given spherical coordinates. @@ -190,7 +190,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * @param {Vec2} sphericalPos - The point on the HUD overlay in spherical coordinates. * @returns {Vec2} The point on the HUD overlay in HUD coordinates. */ - Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> sphericalToOverlay(const glm::vec<2,float,glm::packed_highp> & sphericalPos) const; /*@jsdoc * Gets the spherical coordinates of a 2D point on the HUD overlay. @@ -201,7 +201,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen * @param {Vec2} overlayPos - The point on the HUD overlay in HUD coordinates. * @returns {Vec2} The point on the HUD overlay in spherical coordinates. */ - Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> overlayToSpherical(const glm::vec<2,float,glm::packed_highp> & overlayPos) const; /*@jsdoc * Recenters the HMD HUD to the current HMD position and orientation. @@ -488,9 +488,9 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen bool getAwayStateWhenFocusLostInVREnabled(); QVariant getPlayAreaRect(); - QVector getSensorPositions(); + QVector> getSensorPositions(); - glm::vec3 getPosition() const; + glm::vec<3,float,glm::packed_highp> getPosition() const; private: bool _showTablet { false }; @@ -506,7 +506,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen bool _miniTabletEnabled { true }; // Get the orientation of the HMD - glm::quat getOrientation() const; + glm::qua getOrientation() const; bool getHUDLookAtPosition3D(glm::vec3& result) const; glm::mat4 getWorldHMDMatrix() const; diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp index f8bb1b3fc43..512a95e3213 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.cpp +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -65,6 +65,10 @@ bool KeyboardScriptingInterface::getPreferMalletsOverLasers() const { return DependencyManager::get()->getPreferMalletsOverLasers(); } +void KeyboardScriptingInterface::setPreferMalletsOverLasers(bool mallets) { + return DependencyManager::get()->setPreferMalletsOverLasers(mallets); +} + bool KeyboardScriptingInterface::containsID(const QUuid& id) const { return DependencyManager::get()->containsID(id); } diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h index 2c4c4e39850..508af2c8c41 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.h +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -43,7 +43,7 @@ class KeyboardScriptingInterface : public QObject, public Dependency { Q_PROPERTY(bool raised READ isRaised WRITE setRaised) Q_PROPERTY(bool password READ isPassword WRITE setPassword) Q_PROPERTY(bool use3DKeyboard READ getUse3DKeyboard CONSTANT); - Q_PROPERTY(bool preferMalletsOverLasers READ getPreferMalletsOverLasers CONSTANT) + Q_PROPERTY(bool preferMalletsOverLasers READ getPreferMalletsOverLasers WRITE setPreferMalletsOverLasers) public: KeyboardScriptingInterface() = default; @@ -141,6 +141,7 @@ class KeyboardScriptingInterface : public QObject, public Dependency { private: bool getPreferMalletsOverLasers() const; + void setPreferMalletsOverLasers(bool mallets); bool isRaised() const; void setRaised(bool raised); diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 9829ab8343f..4fe93987f32 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -68,8 +68,8 @@ bool MenuScriptingInterface::menuExists(const QString& menu) { bool result { false }; BLOCKING_INVOKE_METHOD(menuInstance, "menuExists", - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menu)); + Q_GENERIC_RETURN_ARG(bool, result), + Q_GENERIC_ARG(const QString&, menu)); return result; } @@ -131,8 +131,8 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& return; } QMetaObject::invokeMethod(menuInstance, "removeMenuItem", - Q_ARG(const QString&, menu), - Q_ARG(const QString&, menuitem)); + Q_GENERIC_ARG(const QString&, menu), + Q_GENERIC_ARG(const QString&, menuitem)); }; bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { @@ -148,9 +148,9 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& bool result { false }; BLOCKING_INVOKE_METHOD(menuInstance, "menuItemExists", - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menu), - Q_ARG(const QString&, menuitem)); + Q_GENERIC_RETURN_ARG(bool, result), + Q_GENERIC_ARG(const QString&, menu), + Q_GENERIC_ARG(const QString&, menuitem)); return result; } @@ -168,8 +168,8 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { bool result { false }; BLOCKING_INVOKE_METHOD(menuInstance, "isOptionChecked", - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menuOption)); + Q_GENERIC_RETURN_ARG(bool, result), + Q_GENERIC_ARG(const QString&, menuOption)); return result; } @@ -180,8 +180,8 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool } QMetaObject::invokeMethod(menuInstance, "setIsOptionChecked", - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); + Q_GENERIC_ARG(const QString&, menuOption), + Q_GENERIC_ARG(bool, isChecked)); } bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) { @@ -197,8 +197,8 @@ bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) { bool result { false }; BLOCKING_INVOKE_METHOD(menuInstance, "isMenuEnabled", - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menuOption)); + Q_GENERIC_RETURN_ARG(bool, result), + Q_GENERIC_ARG(const QString&, menuOption)); return result; } @@ -210,8 +210,8 @@ void MenuScriptingInterface::setMenuEnabled(const QString& menuOption, bool isCh } QMetaObject::invokeMethod(menuInstance, "setMenuEnabled", - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); + Q_GENERIC_ARG(const QString&, menuOption), + Q_GENERIC_ARG(bool, isChecked)); } void MenuScriptingInterface::triggerOption(const QString& menuOption) { @@ -220,7 +220,7 @@ void MenuScriptingInterface::triggerOption(const QString& menuOption) { return; } - QMetaObject::invokeMethod(menuInstance, "triggerOption", Q_ARG(const QString&, menuOption)); + QMetaObject::invokeMethod(menuInstance, "triggerOption", Q_GENERIC_ARG(const QString&, menuOption)); } void MenuScriptingInterface::setVisible(bool visible) { diff --git a/interface/src/scripting/SettingsScriptingInterface.cpp b/interface/src/scripting/SettingsScriptingInterface.cpp index 00cdf009eb6..7a60f01dfa6 100644 --- a/interface/src/scripting/SettingsScriptingInterface.cpp +++ b/interface/src/scripting/SettingsScriptingInterface.cpp @@ -50,13 +50,11 @@ void SettingsScriptingInterface::setValue(const QString& setting, const QVariant if (_restrictPrivateValues) { qWarning() << "SettingsScriptingInterface::setValue -- restricted write: " << setting << value; return; - } else { - qInfo() << "SettingsScriptingInterface::setValue -- allowing restricted write: " << setting << value; } } // Make a deep-copy of the string. // Dangling pointers can occur with QStrings that are implicitly shared from a ScriptEngine. - QString deepCopy = QString::fromUtf16(setting.utf16()); + QString deepCopy = QString(setting.utf16()); Setting::Handle(deepCopy).set(value); emit valueChanged(setting, value); } diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index bc3f59e9174..10d9bc3cd0c 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -66,7 +66,7 @@ void TestScriptingInterface::waitIdle() { bool TestScriptingInterface::loadTestScene(QString scene) { if (QThread::currentThread() != thread()) { bool result; - BLOCKING_INVOKE_METHOD(this, "loadTestScene", Q_RETURN_ARG(bool, result), Q_ARG(QString, scene)); + BLOCKING_INVOKE_METHOD(this, "loadTestScene", Q_GENERIC_RETURN_ARG(bool, result), Q_GENERIC_ARG(QString, scene)); return result; } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 81d88451e3d..7b40c84c026 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -63,9 +63,7 @@ WindowScriptingInterface::WindowScriptingInterface() { emit interstitialModeChanged(interstitialMode); }); - connect(qApp, &Application::darkThemePreferenceChanged, [this](bool useDarkTheme) { - emit darkThemePreferenceChanged(useDarkTheme); - }); + connect(qApp->themePrefs(), &ThemePrefs::themeChanged, this, &WindowScriptingInterface::themeChanged); } WindowScriptingInterface::~WindowScriptingInterface() { @@ -517,11 +515,11 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu if (QThread::currentThread() != thread()) { int result; BLOCKING_INVOKE_METHOD(this, "openMessageBox", - Q_RETURN_ARG(int, result), - Q_ARG(QString, title), - Q_ARG(QString, text), - Q_ARG(int, buttons), - Q_ARG(int, defaultButton)); + Q_GENERIC_RETURN_ARG(int, result), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QString, text), + Q_GENERIC_ARG(int, buttons), + Q_GENERIC_ARG(int, defaultButton)); return result; } @@ -677,6 +675,42 @@ void WindowScriptingInterface::openWebBrowser(const QString& url) { }); } -bool WindowScriptingInterface::getDarkThemePreference() { - return qApp->getDarkThemePreference(); +bool WindowScriptingInterface::getDarkMode() const { + return qApp->themePrefs()->getDarkMode(); +} + +bool WindowScriptingInterface::getHighContrast() const { + return qApp->themePrefs()->getHighContrast(); +} + +bool WindowScriptingInterface::getReducedMotion() const { + return qApp->themePrefs()->getReducedMotion(); +} + +bool WindowScriptingInterface::getUseSystemColorScheme() const { + return qApp->themePrefs()->getUseSystemColorScheme(); +} + +bool WindowScriptingInterface::getUseSystemContrast() const { + return qApp->themePrefs()->getUseSystemContrast(); +} + +void WindowScriptingInterface::setDarkMode(bool value) { + qApp->themePrefs()->setDarkMode(value); +} + +void WindowScriptingInterface::setHighContrast(bool value) { + qApp->themePrefs()->setHighContrast(value); +} + +void WindowScriptingInterface::setReducedMotion(bool value) { + qApp->themePrefs()->setReducedMotion(value); +} + +void WindowScriptingInterface::setUseSystemColorScheme(bool value) { + qApp->themePrefs()->setUseSystemColorScheme(value); +} + +void WindowScriptingInterface::setUseSystemContrast(bool value) { + qApp->themePrefs()->setUseSystemContrast(value); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index af7f25ea9ed..6be29fd4b7d 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -25,6 +25,8 @@ #include #include +#include "ThemePrefs.h" + /*@jsdoc * The Window API provides various facilities not covered elsewhere, including: window dimensions, window focus, @@ -58,6 +60,13 @@ class WindowScriptingInterface : public QObject, protected Scriptable, public De Q_PROPERTY(int y READ getY) Q_PROPERTY(bool interstitialModeEnabled READ getInterstitialModeEnabled WRITE setInterstitialModeEnabled) + Q_PROPERTY(bool darkMode READ getDarkMode WRITE setDarkMode NOTIFY themeChanged) + Q_PROPERTY(bool highContrast READ getHighContrast WRITE setHighContrast NOTIFY themeChanged) + Q_PROPERTY(bool reducedMotion READ getReducedMotion WRITE setReducedMotion NOTIFY themeChanged) + + Q_PROPERTY(bool useSystemColorScheme READ getUseSystemColorScheme WRITE setUseSystemColorScheme NOTIFY themeChanged) + Q_PROPERTY(bool useSystemContrast READ getUseSystemContrast WRITE setUseSystemContrast NOTIFY themeChanged) + public: WindowScriptingInterface(); ~WindowScriptingInterface(); @@ -400,7 +409,7 @@ public slots: * ".jpeg", ".png", or ".webp" — or if not provided then in the format chosen in general settings, * Default is PNG. Animated images are saved in GIF format.

*/ - void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString()); + void takeSecondaryCamera360Snapshot(const glm::vec<3,float,glm::packed_highp>& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString()); /*@jsdoc * Emits a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that @@ -483,7 +492,7 @@ public slots: * @function Window.getDeviceSize * @returns {Vec2} The width and height of the Interface window or HMD rendering surface, in pixels. */ - glm::vec2 getDeviceSize() const; + glm::vec<2,float,glm::packed_highp> getDeviceSize() const; /*@jsdoc * Gets the last domain connection error when a connection is refused. @@ -626,13 +635,19 @@ public slots: */ void openWebBrowser(const QString& url = ""); - /*@jsdoc - * Returns true if the user prefers a dark UI theme. - * @function Window.getDarkThemePreference - * @returns {boolean} - */ - bool getDarkThemePreference(); + bool getDarkMode() const; + bool getHighContrast() const; + bool getReducedMotion() const; + + bool getUseSystemColorScheme() const; + bool getUseSystemContrast() const; + + void setDarkMode(bool value); + void setHighContrast(bool value); + void setReducedMotion(bool value); + void setUseSystemColorScheme(bool value); + void setUseSystemContrast(bool value); private slots: void onWindowGeometryChanged(const QRect& geometry); @@ -849,11 +864,10 @@ private slots: void minimizedChanged(bool isMinimized); /*@jsdoc - * Triggered when the dark theme preference is changed. - * @function Window.darkThemePreferenceChanged - * @param {boolean} useDarkTheme + * Triggered when the theme preferences are changed. + * @function Window.themeChanged */ - void darkThemePreferenceChanged(bool useDarkTheme); + void themeChanged(); private: QString getPreviousBrowseLocation() const; diff --git a/interface/src/ui/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index c018e6508cf..7b2b912c520 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -180,14 +181,16 @@ Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { } void Highlighter::highlightBlock(const QString& text) { - QRegExp expression(BOLD_PATTERN); + QRegularExpression expression(BOLD_PATTERN); - int index = text.indexOf(expression, 0); + auto expressionMatch = expression.match(text); + qsizetype index = expressionMatch.capturedStart(); while (index >= 0) { - int length = expression.matchedLength(); + qsizetype length = expressionMatch.capturedLength(); setFormat(index, length, boldFormat); - index = text.indexOf(expression, index + length); + expressionMatch = expression.match(text, index + length); + index = expressionMatch.capturedStart(); } if (keyword.isNull() || keyword.isEmpty()) { diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 46ca7bbe039..6827192c5eb 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -163,7 +163,12 @@ void DialogsManager::showUpdateDialog() { void DialogsManager::octreeStatsDetails() { if (!_octreeStatsDialog) { - _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats()); + auto octreeSceneStats = qApp->getOcteeSceneStats(); + if (!octreeSceneStats) { + qCritical() << "DialogsManager::octreeStatsDetails(): Octree scene stats object not available yet. This should never happen."; + std::abort(); + } + _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), octreeSceneStats.value()); if (_hmdToolsDialog) { _hmdToolsDialog->watchWindow(_octreeStatsDialog->windowHandle()); diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index 702a24fd1a9..bb1b5afb488 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -11,7 +11,6 @@ #include "HMDToolsDialog.h" -#include #include #include #include diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index 2761b1c9f07..8cad284ffe2 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -34,7 +34,7 @@ #include "MainWindow.h" #ifdef Q_OS_WIN -#include +#include #endif STATIC_SCRIPT_TYPES_INITIALIZER(+[](ScriptManager* manager){ @@ -421,7 +421,7 @@ void InteractiveWindow::close() { if (_dockWidget) { auto window = qApp->getWindow(); if (QThread::currentThread() != window->thread()) { - BLOCKING_INVOKE_METHOD(window, "removeDockWidget", Q_ARG(QDockWidget*, _dockWidget.get())); + BLOCKING_INVOKE_METHOD(window, "removeDockWidget", Q_GENERIC_ARG(QDockWidget*, _dockWidget.get())); } else { window->removeDockWidget(_dockWidget.get()); } diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index 3d69185ecfc..1c98c4d0cde 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -186,10 +186,10 @@ class InteractiveWindow : public QObject { Q_OBJECT Q_PROPERTY(QString title READ getTitle WRITE setTitle) - Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> position READ getPosition WRITE setPosition) Q_PROPERTY(RelativePositionAnchor relativePositionAnchor READ getRelativePositionAnchor WRITE setRelativePositionAnchor) - Q_PROPERTY(glm::vec2 relativePosition READ getRelativePosition WRITE setRelativePosition) - Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> relativePosition READ getRelativePosition WRITE setRelativePosition) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> size READ getSize WRITE setSize) Q_PROPERTY(bool visible READ isVisible WRITE setVisible) Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode) @@ -202,8 +202,8 @@ class InteractiveWindow : public QObject { Q_INVOKABLE QString getTitle() const; Q_INVOKABLE void setTitle(const QString& title); - Q_INVOKABLE glm::vec2 getPosition() const; - Q_INVOKABLE void setPosition(const glm::vec2& position); + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> getPosition() const; + Q_INVOKABLE void setPosition(const glm::vec<2,float,glm::packed_highp>& position); RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::NO_ANCHOR }; Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const; @@ -212,16 +212,16 @@ class InteractiveWindow : public QObject { // This "relative position" is relative to the "relative position anchor" and excludes the window frame. // This position will ALWAYS include the geometry of a docked widget, if one is present. glm::vec2 _relativePosition{ 0.0f, 0.0f }; - Q_INVOKABLE glm::vec2 getRelativePosition() const; - Q_INVOKABLE void setRelativePosition(const glm::vec2& position); + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> getRelativePosition() const; + Q_INVOKABLE void setRelativePosition(const glm::vec<2,float,glm::packed_highp>& position); Q_INVOKABLE void setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry); bool _isFullScreenWindow{ false }; Q_INVOKABLE void repositionAndResizeFullScreenWindow(); - Q_INVOKABLE glm::vec2 getSize() const; - Q_INVOKABLE void setSize(const glm::vec2& size); + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> getSize() const; + Q_INVOKABLE void setSize(const glm::vec<2,float,glm::packed_highp>& size); Q_INVOKABLE void setVisible(bool visible); Q_INVOKABLE bool isVisible() const; @@ -263,7 +263,7 @@ public slots: * // QML file, "InteractiveWindow.qml". * * import QtQuick 2.5 - * import QtQuick.Controls 1.4 + * import QtQuick.Controls 2.3 * * Rectangle { * diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index e0c404fba13..f3e9abc6897 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -64,7 +64,7 @@ void _writeLines(const QString& filename, const QList& lines) { file.open(QFile::WriteOnly); auto root = QJsonObject(); root["version"] = 1.0; - root["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + root["last-modified"] = QDateTime::currentDateTime().toOffsetFromUtc(Qt::OffsetFromUTC).toString(Qt::ISODate); root[JSON_KEY] = QJsonArray::fromStringList(lines); auto json = QJsonDocument(root).toJson(); QTextStream(&file) << json; @@ -129,13 +129,13 @@ JSConsole::JSConsole(QWidget* parent, const ScriptManagerPointer& scriptManager) QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), - _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), + _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + HISTORY_FILENAME), _commandHistory(_readLines(_savedHistoryFilename)), _completer(new QCompleter(this)), _monospaceFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), // unfortunately we'll just have to use the first theme we get, // because of the custom colored widgets that can't easily be recolored later - _lightTheme(!qApp->getDarkThemePreference()) { + _lightTheme(!qApp->themePrefs()->getDarkMode()) { readAPI(); @@ -512,9 +512,9 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { const int MODULE_INDEX = 3; const int PROPERTY_INDEX = 4; // TODO: disallow invalid characters on left of property - QRegExp regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); - regExp.indexIn(leftOfCursor); - auto rexExpCapturedTexts = regExp.capturedTexts(); + QRegularExpression regExp("((([A-Za-z0-9_\\.]+)\\.)|(?!\\.))([a-zA-Z0-9_]*)$"); + auto regExpMatch = regExp.match(leftOfCursor); + auto rexExpCapturedTexts = regExpMatch.capturedTexts(); auto memberOf = rexExpCapturedTexts[MODULE_INDEX]; completionPrefix = rexExpCapturedTexts[PROPERTY_INDEX]; bool switchedModule = false; @@ -591,7 +591,8 @@ void JSConsole::scrollToBottom() { void JSConsole::appendMessage(const QString& gutter, const QString& message, const QColor& fgColor, const QColor& bgColor) { QWidget* logLine = new QWidget(_ui->logArea); QHBoxLayout* layout = new QHBoxLayout(logLine); - layout->setMargin(0); + // QT6TODO: + //layout->setMargin(0); layout->setSpacing(4); QLabel* gutterLabel = new QLabel(logLine); diff --git a/interface/src/ui/LogDialog.cpp b/interface/src/ui/LogDialog.cpp index 65a0d2ea37b..c6379cf8ed7 100644 --- a/interface/src/ui/LogDialog.cpp +++ b/interface/src/ui/LogDialog.cpp @@ -61,7 +61,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _debugPrintBox->setCheckState(Qt::Checked); } _debugPrintBox->show(); - connect(_debugPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleDebugPrintBox); + connect(_debugPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleDebugPrintBox); _leftPad += DEBUG_CHECKBOX_WIDTH + BUTTON_MARGIN; _infoPrintBox = new QCheckBox("INFO", this); @@ -70,7 +70,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _infoPrintBox->setCheckState(Qt::Checked); } _infoPrintBox->show(); - connect(_infoPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleInfoPrintBox); + connect(_infoPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleInfoPrintBox); _leftPad += INFO_CHECKBOX_WIDTH + BUTTON_MARGIN; _criticalPrintBox = new QCheckBox("CRITICAL", this); @@ -79,7 +79,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _criticalPrintBox->setCheckState(Qt::Checked); } _criticalPrintBox->show(); - connect(_criticalPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleCriticalPrintBox); + connect(_criticalPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleCriticalPrintBox); _leftPad += CRITICAL_CHECKBOX_WIDTH + BUTTON_MARGIN; _warningPrintBox = new QCheckBox("WARNING", this); @@ -88,7 +88,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _warningPrintBox->setCheckState(Qt::Checked); } _warningPrintBox->show(); - connect(_warningPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleWarningPrintBox); + connect(_warningPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleWarningPrintBox); _leftPad += WARNING_CHECKBOX_WIDTH + BUTTON_MARGIN; _suppressPrintBox = new QCheckBox("SUPPRESS", this); @@ -97,7 +97,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _suppressPrintBox->setCheckState(Qt::Checked); } _suppressPrintBox->show(); - connect(_suppressPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleSuppressPrintBox); + connect(_suppressPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleSuppressPrintBox); _leftPad += SUPPRESS_CHECKBOX_WIDTH + BUTTON_MARGIN; _fatalPrintBox = new QCheckBox("FATAL", this); @@ -106,7 +106,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _fatalPrintBox->setCheckState(Qt::Checked); } _fatalPrintBox->show(); - connect(_fatalPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleFatalPrintBox); + connect(_fatalPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleFatalPrintBox); _leftPad += FATAL_CHECKBOX_WIDTH + BUTTON_MARGIN; _unknownPrintBox = new QCheckBox("UNKNOWN", this); @@ -115,7 +115,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _unknownPrintBox->setCheckState(Qt::Checked); } _unknownPrintBox->show(); - connect(_unknownPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleUnknownPrintBox); + connect(_unknownPrintBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleUnknownPrintBox); _leftPad = MARGIN_LEFT; _filterDropdown = new QComboBox(this); @@ -156,7 +156,7 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog #ifdef Q_OS_WIN connect(_keepOnTopBox, &QCheckBox::stateChanged, qApp, &Application::recreateLogWindow); #else - connect(_keepOnTopBox, &QCheckBox::stateChanged, this, &LogDialog::handleKeepWindowOnTop); + connect(_keepOnTopBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleKeepWindowOnTop); #endif _keepOnTopBox->show(); @@ -165,14 +165,14 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _extraDebuggingBox->setCheckState(Qt::Checked); } _extraDebuggingBox->show(); - connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox); + connect(_extraDebuggingBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleExtraDebuggingCheckbox); _showSourceDebuggingBox = new QCheckBox("Show script sources", this); if (_logger->showSourceDebugging()) { _showSourceDebuggingBox->setCheckState(Qt::Checked); } _showSourceDebuggingBox->show(); - connect(_showSourceDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleShowSourceDebuggingCheckbox); + connect(_showSourceDebuggingBox, &QCheckBox::checkStateChanged, this, &LogDialog::handleShowSourceDebuggingCheckbox); _allLogsButton = new QPushButton("All Messages", this); // set object name for css styling diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index cd706d0bcb5..aa9a64b32ec 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index f68f5c8d927..24ec20f2caa 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -188,35 +188,37 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { unsigned long totalInternal = 0; unsigned long totalLeaves = 0; - NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); - sceneStats->withReadLock([&] { - for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { - //const QUuid& uuid = i->first; - OctreeSceneStats& stats = i->second; - serverCount++; - - // calculate server node totals - totalNodes += stats.getTotalElements(); - totalInternal += stats.getTotalInternal(); - totalLeaves += stats.getTotalLeaves(); - - // Sending mode - if (serverCount > 1) { - sendingMode << ","; - } - if (stats.isMoving()) { - sendingMode << "M"; - movingServerCount++; - } else { - sendingMode << "S"; - } - if (stats.isFullScene()) { - sendingMode << "F"; - } else { - sendingMode << "p"; + auto sceneStats = qApp->getOcteeSceneStats(); + if (sceneStats) { + sceneStats.value()->withReadLock([&] { + for (NodeToOctreeSceneStatsIterator i = sceneStats.value()->begin(); i != sceneStats.value()->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + + // calculate server node totals + totalNodes += stats.getTotalElements(); + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + + // Sending mode + if (serverCount > 1) { + sendingMode << ","; + } + if (stats.isMoving()) { + sendingMode << "M"; + movingServerCount++; + } else { + sendingMode << "S"; + } + if (stats.isFullScene()) { + sendingMode << "F"; + } else { + sendingMode << "p"; + } } - } - }); + }); + } sendingMode << " - " << serverCount << " servers"; if (movingServerCount > 0) { sendingMode << " "; @@ -388,114 +390,116 @@ void OctreeStatsDialog::showOctreeServersOfType(NodeType_t serverType) { // now lookup stats details for this server... if (_extraServerDetails != LESS) { - NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); - sceneStats->withReadLock([&] { - if (sceneStats->find(nodeUUID) != sceneStats->end()) { - OctreeSceneStats& stats = sceneStats->at(nodeUUID); - - switch (_extraServerDetails) { - case MOST: { - extraDetails << "
"; - - float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; - float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; - float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; - float lastFullPackets = stats.getLastFullTotalPackets(); - float lastFullPPS = lastFullPackets; - if (lastFullSendInSeconds > 0) { - lastFullPPS = lastFullPackets / lastFullSendInSeconds; - } - - QString lastFullEncodeString = locale.toString(lastFullEncode); - QString lastFullSendString = locale.toString(lastFullSend); - QString lastFullPacketsString = locale.toString(lastFullPackets); - QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); - QString lastFullPPSString = locale.toString(lastFullPPS); - - extraDetails << "
" << "Last Full Scene... " << - "Encode: " << qPrintable(lastFullEncodeString) << " ms " << - "Send: " << qPrintable(lastFullSendString) << " ms " << - "Packets: " << qPrintable(lastFullPacketsString) << " " << - "Bytes: " << qPrintable(lastFullBytesString) << " " << - "Rate: " << qPrintable(lastFullPPSString) << " PPS"; - - for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { - OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); - OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); - extraDetails << "
" << itemInfo.caption << " " << stats.getItemValue(item); - } - } // fall through... since MOST has all of MORE - /* fall-thru */ - case MORE: { - QString totalString = locale.toString((uint)stats.getTotalElements()); - QString internalString = locale.toString((uint)stats.getTotalInternal()); - QString leavesString = locale.toString((uint)stats.getTotalLeaves()); - - serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; - - serverDetails << "
" << "Elements: " << - qPrintable(totalString) << " total " << - qPrintable(internalString) << " internal " << - qPrintable(leavesString) << " leaves "; - - QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); - QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); - QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); - const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); - QString incomingOutOfOrderString = locale.toString((uint)seqStats.getOutOfOrder()); - QString incomingLateString = locale.toString((uint)seqStats.getLate()); - QString incomingUnreasonableString = locale.toString((uint)seqStats.getUnreasonable()); - QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); - QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); - QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); - - qint64 clockSkewInUsecs = node->getClockSkewUsec(); - QString formattedClockSkewString = formatUsecTime(clockSkewInUsecs); - qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; - QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); - QString incomingPingTimeString = locale.toString(node->getPingMs()); - QString incomingClockSkewString = locale.toString(clockSkewInMS); - - serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << - "/ Lost: " << qPrintable(incomingLikelyLostString) << - "/ Recovered: " << qPrintable(incomingRecovered); - - serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << - "/ Early: " << qPrintable(incomingEarlyString) << - "/ Late: " << qPrintable(incomingLateString) << - "/ Unreasonable: " << qPrintable(incomingUnreasonableString); - - serverDetails << "
" << - " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; - - serverDetails << "
" << - " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; - - serverDetails << "
" << - " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" << - " [" << qPrintable(formattedClockSkewString) << "]"; - - - serverDetails << "
" << "Incoming" << - " Bytes: " << qPrintable(incomingBytesString) << - " Wasted Bytes: " << qPrintable(incomingWastedBytesString); - - serverDetails << extraDetails.str(); - if (_extraServerDetails == MORE) { - linkDetails << " [most...]"; - linkDetails << " [less...]"; - } else { - linkDetails << " [less...]"; - linkDetails << " [least...]"; - } - - } break; - case LESS: { - // nothing - } break; + auto sceneStats = qApp->getOcteeSceneStats(); + if (sceneStats) { + sceneStats.value()->withReadLock([&] { + if (sceneStats.value()->find(nodeUUID) != sceneStats.value()->end()) { + OctreeSceneStats& stats = sceneStats.value()->at(nodeUUID); + + switch (_extraServerDetails) { + case MOST: { + extraDetails << "
"; + + float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; + float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; + } + + QString lastFullEncodeString = locale.toString(lastFullEncode); + QString lastFullSendString = locale.toString(lastFullSend); + QString lastFullPacketsString = locale.toString(lastFullPackets); + QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); + QString lastFullPPSString = locale.toString(lastFullPPS); + + extraDetails << "
" << "Last Full Scene... " << + "Encode: " << qPrintable(lastFullEncodeString) << " ms " << + "Send: " << qPrintable(lastFullSendString) << " ms " << + "Packets: " << qPrintable(lastFullPacketsString) << " " << + "Bytes: " << qPrintable(lastFullBytesString) << " " << + "Rate: " << qPrintable(lastFullPPSString) << " PPS"; + + for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { + OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); + OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); + extraDetails << "
" << itemInfo.caption << " " << stats.getItemValue(item); + } + } // fall through... since MOST has all of MORE + /* fall-thru */ + case MORE: { + QString totalString = locale.toString((uint)stats.getTotalElements()); + QString internalString = locale.toString((uint)stats.getTotalInternal()); + QString leavesString = locale.toString((uint)stats.getTotalLeaves()); + + serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; + + serverDetails << "
" << "Elements: " << + qPrintable(totalString) << " total " << + qPrintable(internalString) << " internal " << + qPrintable(leavesString) << " leaves "; + + QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); + QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); + QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); + const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); + QString incomingOutOfOrderString = locale.toString((uint)seqStats.getOutOfOrder()); + QString incomingLateString = locale.toString((uint)seqStats.getLate()); + QString incomingUnreasonableString = locale.toString((uint)seqStats.getUnreasonable()); + QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); + QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); + QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); + + qint64 clockSkewInUsecs = node->getClockSkewUsec(); + QString formattedClockSkewString = formatUsecTime(clockSkewInUsecs); + qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; + QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); + QString incomingPingTimeString = locale.toString(node->getPingMs()); + QString incomingClockSkewString = locale.toString(clockSkewInMS); + + serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << + "/ Lost: " << qPrintable(incomingLikelyLostString) << + "/ Recovered: " << qPrintable(incomingRecovered); + + serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << + "/ Early: " << qPrintable(incomingEarlyString) << + "/ Late: " << qPrintable(incomingLateString) << + "/ Unreasonable: " << qPrintable(incomingUnreasonableString); + + serverDetails << "
" << + " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; + + serverDetails << "
" << + " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; + + serverDetails << "
" << + " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" << + " [" << qPrintable(formattedClockSkewString) << "]"; + + + serverDetails << "
" << "Incoming" << + " Bytes: " << qPrintable(incomingBytesString) << + " Wasted Bytes: " << qPrintable(incomingWastedBytesString); + + serverDetails << extraDetails.str(); + if (_extraServerDetails == MORE) { + linkDetails << " [most...]"; + linkDetails << " [less...]"; + } else { + linkDetails << " [less...]"; + linkDetails << " [least...]"; + } + + } break; + case LESS: { + // nothing + } break; + } } - } - }); + }); + } } else { linkDetails << " [more...]"; linkDetails << " [most...]"; diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index a4ab8e2f19c..064bdcd4c96 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -114,35 +114,37 @@ void OctreeStatsProvider::updateOctreeStatsData() { unsigned long totalLeaves = 0; m_sendingMode.clear(); - NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); - sceneStats->withReadLock([&] { - for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { - //const QUuid& uuid = i->first; - OctreeSceneStats& stats = i->second; - serverCount++; - - // calculate server node totals - totalNodes += stats.getTotalElements(); - totalInternal += stats.getTotalInternal(); - totalLeaves += stats.getTotalLeaves(); - - // Sending mode - if (serverCount > 1) { - m_sendingMode += ","; - } - if (stats.isMoving()) { - m_sendingMode += "M"; - movingServerCount++; - } else { - m_sendingMode += "S"; - } - if (stats.isFullScene()) { - m_sendingMode += "F"; - } else { - m_sendingMode += "p"; + auto sceneStats = qApp->getOcteeSceneStats(); + if (sceneStats) { + sceneStats.value()->withReadLock([&] { + for (NodeToOctreeSceneStatsIterator i = sceneStats.value()->begin(); i != sceneStats.value()->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + + // calculate server node totals + totalNodes += stats.getTotalElements(); + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + + // Sending mode + if (serverCount > 1) { + m_sendingMode += ","; + } + if (stats.isMoving()) { + m_sendingMode += "M"; + movingServerCount++; + } else { + m_sendingMode += "S"; + } + if (stats.isFullScene()) { + m_sendingMode += "F"; + } else { + m_sendingMode += "p"; + } } - } - }); + }); + } m_sendingMode += QString(" - %1 servers").arg(serverCount); if (movingServerCount > 0) { m_sendingMode += " "; @@ -261,72 +263,74 @@ void OctreeStatsProvider::showOctreeServersOfType(NodeType_t serverType) { QUuid nodeUUID = node->getUUID(); // now lookup stats details for this server... - NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); - sceneStats->withReadLock([&] { - if (sceneStats->find(nodeUUID) != sceneStats->end()) { - OctreeSceneStats& stats = sceneStats->at(nodeUUID); - - float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; - float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; - float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; - float lastFullPackets = stats.getLastFullTotalPackets(); - float lastFullPPS = lastFullPackets; - if (lastFullSendInSeconds > 0) { - lastFullPPS = lastFullPackets / lastFullSendInSeconds; - } - - mostDetails += QString("

Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS") - .arg(lastFullEncode) - .arg(lastFullSend) - .arg(lastFullPackets) - .arg(stats.getLastFullTotalBytes()) - .arg(lastFullPPS); - - for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { - OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); - OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); - mostDetails += QString("
%1 %2") - .arg(itemInfo.caption).arg(stats.getItemValue(item)); + auto sceneStats = qApp->getOcteeSceneStats(); + if (sceneStats) { + sceneStats.value()->withReadLock([&] { + if (sceneStats.value()->find(nodeUUID) != sceneStats.value()->end()) { + OctreeSceneStats& stats = sceneStats.value()->at(nodeUUID); + + float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; + float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; + } + + mostDetails += QString("

Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS") + .arg(lastFullEncode) + .arg(lastFullSend) + .arg(lastFullPackets) + .arg(stats.getLastFullTotalBytes()) + .arg(lastFullPPS); + + for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { + OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); + OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); + mostDetails += QString("
%1 %2") + .arg(itemInfo.caption).arg(stats.getItemValue(item)); + } + + moreDetails += "
Node UUID: " +nodeUUID.toString() + " "; + + moreDetails += QString("
Elements: %1 total %2 internal %3 leaves ") + .arg(stats.getTotalElements()) + .arg(stats.getTotalInternal()) + .arg(stats.getTotalLeaves()); + + const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); + qint64 clockSkewInUsecs = node->getClockSkewUsec(); + qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; + + moreDetails += QString("
Incoming Packets: %1/ Lost: %2/ Recovered: %3") + .arg(stats.getIncomingPackets()) + .arg(seqStats.getLost()) + .arg(seqStats.getRecovered()); + + moreDetails += QString("
Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4") + .arg(seqStats.getOutOfOrder()) + .arg(seqStats.getEarly()) + .arg(seqStats.getLate()) + .arg(seqStats.getUnreasonable()); + + moreDetails += QString("
Average Flight Time: %1 msecs") + .arg(stats.getIncomingFlightTimeAverage()); + + moreDetails += QString("
Average Ping Time: %1 msecs") + .arg(node->getPingMs()); + + moreDetails += QString("
Average Clock Skew: %1 msecs [%2]") + .arg(clockSkewInMS) + .arg(formatUsecTime(clockSkewInUsecs)); + + + moreDetails += QString("
Incoming Bytes: %1 Wasted Bytes: %2") + .arg(stats.getIncomingBytes()) + .arg(stats.getIncomingWastedBytes()); } - - moreDetails += "
Node UUID: " +nodeUUID.toString() + " "; - - moreDetails += QString("
Elements: %1 total %2 internal %3 leaves ") - .arg(stats.getTotalElements()) - .arg(stats.getTotalInternal()) - .arg(stats.getTotalLeaves()); - - const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); - qint64 clockSkewInUsecs = node->getClockSkewUsec(); - qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; - - moreDetails += QString("
Incoming Packets: %1/ Lost: %2/ Recovered: %3") - .arg(stats.getIncomingPackets()) - .arg(seqStats.getLost()) - .arg(seqStats.getRecovered()); - - moreDetails += QString("
Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4") - .arg(seqStats.getOutOfOrder()) - .arg(seqStats.getEarly()) - .arg(seqStats.getLate()) - .arg(seqStats.getUnreasonable()); - - moreDetails += QString("
Average Flight Time: %1 msecs") - .arg(stats.getIncomingFlightTimeAverage()); - - moreDetails += QString("
Average Ping Time: %1 msecs") - .arg(node->getPingMs()); - - moreDetails += QString("
Average Clock Skew: %1 msecs [%2]") - .arg(clockSkewInMS) - .arg(formatUsecTime(clockSkewInUsecs)); - - - moreDetails += QString("
Incoming Bytes: %1 Wasted Bytes: %2") - .arg(stats.getIncomingBytes()) - .arg(stats.getIncomingWastedBytes()); - } - }); + }); + } m_servers.append(lesserDetails); m_servers.append(moreDetails); m_servers.append(mostDetails); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 48f958583cf..480a4c58903 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -138,12 +138,6 @@ void setupPreferences() { // UI static const QString UI_CATEGORY { "User Interface" }; - { - auto getter = []()->bool { return qApp->getDarkThemePreference(); }; - auto setter = [](bool value) { qApp->setDarkThemePreference(value); }; - preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use dark theme", getter, setter)); - } - { auto getter = []()->bool { return qApp->getSettingConstrainToolbarPosition(); }; auto setter = [](bool value) { qApp->setSettingConstrainToolbarPosition(value); }; diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 46ffffe62dd..8b60234df34 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -162,5 +162,6 @@ void ResourceImageItemRenderer::render() { _fboMutex.unlock(); } glFlush(); - _window->resetOpenGLState(); + // QT6TODO: check if this can be omitted + //_window->resetOpenGLState(); } diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 2884574767c..fbd45932a46 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -219,9 +220,9 @@ void Snapshot::takeNextSnapshot() { // Process six QImages if (_cubemapOutputFormat) { - QtConcurrent::run([this]() { convertToCubemap(); }); + QThreadPool::globalInstance()->start([this]() { convertToCubemap(); }); } else { - QtConcurrent::run([this]() { convertToEquirectangular(); }); + QThreadPool::globalInstance()->start([this]() { convertToEquirectangular(); }); } } } @@ -361,7 +362,7 @@ QFile* Snapshot::savedFileForSnapshot(QImage& shot, QString username = DependencyManager::get()->getAccountInfo().getUsername(); // normalize username, replace all non alphanumeric with '-' - username.replace(QRegExp("[^A-Za-z0-9_]"), "-"); + username.replace(QRegularExpression("[^A-Za-z0-9_]"), "-"); QDateTime now = QDateTime::currentDateTime(); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 6d4c36afa1f..0709d0f99ef 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -94,7 +94,7 @@ void SnapshotAnimated::captureFrames() { emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); // Kick off the thread that'll pack the frames into the GIF - QtConcurrent::run(processFrames); + QThreadPool::globalInstance()->start(processFrames); // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE // that the slot will not be called again in the future. // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html diff --git a/interface/src/ui/StandAloneJSConsole.cpp b/interface/src/ui/StandAloneJSConsole.cpp index e6c3748d208..0bc3d653223 100644 --- a/interface/src/ui/StandAloneJSConsole.cpp +++ b/interface/src/ui/StandAloneJSConsole.cpp @@ -26,7 +26,8 @@ void StandAloneJSConsole::toggleConsole() { dialog->setLayout(layout); dialog->resize(QSize(CONSOLE_WIDTH, CONSOLE_HEIGHT)); - layout->setMargin(0); + // QT6TODO: + //layout->setMargin(0); layout->setSpacing(0); layout->addWidget(new JSConsole(dialog)); dialog->setWindowOpacity(CONSOLE_WINDOW_OPACITY); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8306274884a..822724c6e0f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -339,35 +339,39 @@ void Stats::updateStats(bool force) { unsigned long totalLeaves = 0; std::stringstream sendingModeStream(""); sendingModeStream << "["; - NodeToOctreeSceneStats* octreeServerSceneStats = qApp->getOcteeSceneStats(); - for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { - //const QUuid& uuid = i->first; - OctreeSceneStats& stats = i->second; - serverCount++; - if (_expanded) { - if (serverCount > 1) { - sendingModeStream << ","; - } - if (stats.isMoving()) { - sendingModeStream << "M"; - movingServerCount++; - } else { - sendingModeStream << "S"; - } - if (stats.isFullScene()) { - sendingModeStream << "F"; - } - else { - sendingModeStream << "p"; - } - } + auto octreeServerSceneStats = qApp->getOcteeSceneStats(); + if (octreeServerSceneStats) { + octreeServerSceneStats.value()->withReadLock([&] { + for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats.value()->begin(); i != octreeServerSceneStats.value()->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + if (_expanded) { + if (serverCount > 1) { + sendingModeStream << ","; + } + if (stats.isMoving()) { + sendingModeStream << "M"; + movingServerCount++; + } else { + sendingModeStream << "S"; + } + if (stats.isFullScene()) { + sendingModeStream << "F"; + } + else { + sendingModeStream << "p"; + } + } - // calculate server node totals - totalNodes += stats.getTotalElements(); - if (_expanded) { - totalInternal += stats.getTotalInternal(); - totalLeaves += stats.getTotalLeaves(); - } + // calculate server node totals + totalNodes += stats.getTotalElements(); + if (_expanded) { + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + } + } + }); } if (_expanded || force) { if (serverCount == 0) { @@ -505,7 +509,7 @@ void Stats::updateStats(bool force) { } int linesDisplayed = 0; - QMapIterator j(sortedRecords); + QMultiMapIterator j(sortedRecords); j.toBack(); QString perfLines; while (j.hasPrevious()) { diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 7ff2132ab99..68c7d844de3 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -11,7 +11,10 @@ #include "UpdateDialog.h" +#include + #include +#include #include "DependencyManager.h" @@ -38,7 +41,7 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : // grab the release notes for this later version QString releaseNotes = it.value()["releaseNotes"]; releaseNotes.remove("
"); - releaseNotes.remove(QRegExp("^\n+")); + releaseNotes.remove(QRegularExpression("^\n+")); _releaseNotes += "\n" + it.key().versionString + "\n" + releaseNotes + "\n"; } else { break; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index e204b140f5c..19da0e8991d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -201,7 +201,7 @@ QUuid Overlays::addOverlay(const QString& type, const QVariant& properties) { if (QThread::currentThread() != thread()) { QUuid result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QString&, type), Q_ARG(const QVariant&, properties)); + BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_GENERIC_RETURN_ARG(QUuid, result), Q_GENERIC_ARG(const QString&, type), Q_GENERIC_ARG(const QVariant&, properties)); return result; } @@ -250,7 +250,7 @@ QUuid Overlays::cloneOverlay(const QUuid& id) { if (QThread::currentThread() != thread()) { QUuid result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(QUuid, result), Q_ARG(const QUuid&, id)); + BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_GENERIC_RETURN_ARG(QUuid, result), Q_GENERIC_ARG(const QUuid&, id)); return result; } return add2DOverlay(Overlay::Pointer(overlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); })); @@ -347,7 +347,7 @@ QString Overlays::getOverlayType(const QUuid& id) { if (QThread::currentThread() != thread()) { QString result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(const QUuid&, id)); + BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_GENERIC_RETURN_ARG(QString, result), Q_GENERIC_ARG(const QUuid&, id)); return result; } return overlay->getType(); @@ -362,7 +362,7 @@ QObject* Overlays::getOverlayObject(const QUuid& id) { if (QThread::currentThread() != thread()) { QObject* result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(const QUuid&, id)); + BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_GENERIC_RETURN_ARG(QObject*, result), Q_GENERIC_ARG(const QUuid&, id)); return result; } return qobject_cast(&(*overlay)); @@ -378,7 +378,8 @@ QUuid Overlays::getOverlayAtPoint(const glm::vec2& point) { if (QThread::currentThread() != thread()) { QUuid result; - BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(QUuid, result), Q_ARG(const glm::vec2&, point)); + BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_GENERIC_RETURN_ARG(QUuid, result), + QArgument& >("const glm::vec<2,float,glm::packed_highp>&", point)); return result; } @@ -520,7 +521,7 @@ QSizeF Overlays::textSize(const QUuid& id, const QString& text) { if (QThread::currentThread() != thread()) { QSizeF result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(const QUuid&, id), Q_ARG(QString, text)); + BLOCKING_INVOKE_METHOD(this, "textSize", Q_GENERIC_RETURN_ARG(QSizeF, result), Q_GENERIC_ARG(const QUuid&, id), Q_GENERIC_ARG(QString, text)); return result; } if (auto textOverlay = std::dynamic_pointer_cast(overlay)) { @@ -569,7 +570,7 @@ float Overlays::width() { if (QThread::currentThread() != thread()) { float result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "width", Q_RETURN_ARG(float, result)); + BLOCKING_INVOKE_METHOD(this, "width", Q_GENERIC_RETURN_ARG(float, result)); return result; } @@ -581,7 +582,7 @@ float Overlays::height() { if (QThread::currentThread() != thread()) { float result; PROFILE_RANGE(script, __FUNCTION__); - BLOCKING_INVOKE_METHOD(this, "height", Q_RETURN_ARG(float, result)); + BLOCKING_INVOKE_METHOD(this, "height", Q_GENERIC_RETURN_ARG(float, result)); return result; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 9b12a4f6ccd..aabdeaeba94 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -306,7 +306,7 @@ public slots: * print("Clicked: " + overlay); * }); */ - QUuid getOverlayAtPoint(const glm::vec2& point); + QUuid getOverlayAtPoint(const glm::vec<2,float,glm::packed_highp>& point); /*@jsdoc * Finds the closest 3D overlay (or local entity) intersected by a {@link PickRay}. @@ -371,7 +371,7 @@ public slots: * var overlaysFound = Overlays.findOverlays(MyAvatar.position, 5.0); * print("Overlays found: " + JSON.stringify(overlaysFound)); */ - QVector findOverlays(const glm::vec3& center, float radius); + QVector findOverlays(const glm::vec<3,float,glm::packed_highp>& center, float radius); /*@jsdoc * Checks whether an overlay's (or entity's) assets have been loaded. For example, for an diff --git a/interface/src/webbrowser/WebBrowserSuggestionsEngine.cpp b/interface/src/webbrowser/WebBrowserSuggestionsEngine.cpp index 4e7b135cdff..5163874a5cc 100644 --- a/interface/src/webbrowser/WebBrowserSuggestionsEngine.cpp +++ b/interface/src/webbrowser/WebBrowserSuggestionsEngine.cpp @@ -67,7 +67,7 @@ void WebBrowserSuggestionsEngine::suggestionsFinished(QNetworkReply *reply) { QJsonDocument json = QJsonDocument::fromJson(response, &err); const QVariant res = json.toVariant(); - if (err.error != QJsonParseError::NoError || res.type() != QVariant::List) { + if (err.error != QJsonParseError::NoError || res.typeId() != QMetaType::QVariantList) { return; } diff --git a/libraries/animation/src/AnimExpression.h b/libraries/animation/src/AnimExpression.h index 87fb3ca20cc..cc52060c02b 100644 --- a/libraries/animation/src/AnimExpression.h +++ b/libraries/animation/src/AnimExpression.h @@ -12,6 +12,7 @@ #define hifi_AnimExpression #include +#include #include #include #include diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 066ccec056b..8a655fbe97f 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -1083,11 +1083,10 @@ bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& json AnimNodeLoader::AnimNodeLoader(const QUrl& url) : _url(url) { - _resource = QSharedPointer::create(url); - _resource->setSelf(_resource); + _resource = std::make_shared(url); _resource->setLoadPriorityOperator(this, []() { return ANIM_GRAPH_LOAD_PRIORITY; }); - connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); - connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError); + connect(_resource.get(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); + connect(_resource.get(), &Resource::failed, this, &AnimNodeLoader::onRequestError); _resource->ensureLoading(); } diff --git a/libraries/animation/src/AnimNodeLoader.h b/libraries/animation/src/AnimNodeLoader.h index ac27402cf71..d293133f2fd 100644 --- a/libraries/animation/src/AnimNodeLoader.h +++ b/libraries/animation/src/AnimNodeLoader.h @@ -42,7 +42,7 @@ protected slots: protected: QUrl _url; - QSharedPointer _resource; + std::shared_ptr _resource; private: Q_DISABLE_COPY(AnimNodeLoader) diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index f7112cff73d..6d25b1400e4 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -33,15 +33,19 @@ AnimationCache::AnimationCache(QObject* parent) : } AnimationPointer AnimationCache::getAnimation(const QUrl& url) { - return getResource(url).staticCast(); + auto animation = std::dynamic_pointer_cast(getResource(url)); + Q_ASSERT(animation); + return animation; } -QSharedPointer AnimationCache::createResource(const QUrl& url) { - return QSharedPointer(new Animation(url), &Resource::deleter); +std::shared_ptr AnimationCache::createResource(const QUrl& url) { + return std::shared_ptr(new Animation(url), Resource::sharedPtrDeleter); } -QSharedPointer AnimationCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new Animation(*resource.staticCast()), &Resource::deleter); +std::shared_ptr AnimationCache::createResourceCopy(const std::shared_ptr& resource) { + auto animation = std::dynamic_pointer_cast(resource); + Q_ASSERT(animation); + return std::shared_ptr(new Animation(*animation), Resource::sharedPtrDeleter); } AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) : @@ -73,7 +77,7 @@ void AnimationReader::run() { // Parse the FBX directly from the QNetworkReply HFMModel::Pointer hfmModel; if (_url.path().toLower().endsWith(".fbx")) { - hfmModel = FBXSerializer().read(_data, QVariantHash(), _url.path()); + hfmModel = FBXSerializer().read(_data, hifi::VariantMultiHash(), _url.path()); } else { QString errorStr("usupported format"); emit onError(299, errorStr); @@ -97,7 +101,7 @@ QStringList Animation::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; BLOCKING_INVOKE_METHOD(const_cast(this), "getJointNames", - Q_RETURN_ARG(QStringList, result)); + Q_GENERIC_RETURN_ARG(QStringList, result)); return result; } QStringList names; @@ -113,7 +117,7 @@ QVector Animation::getFrames() const { if (QThread::currentThread() != thread()) { QVector result; BLOCKING_INVOKE_METHOD(const_cast(this), "getFrames", - Q_RETURN_ARG(QVector, result)); + Q_GENERIC_RETURN_ARG(QVector, result)); return result; } if (_hfmModel) { diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index f879a7eea62..bf048b84a75 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -23,7 +23,7 @@ class Animation; -using AnimationPointer = QSharedPointer; +using AnimationPointer = std::shared_ptr; class AnimationCache : public ResourceCache, public Dependency { Q_OBJECT @@ -35,8 +35,8 @@ class AnimationCache : public ResourceCache, public Dependency { Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url); protected: - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; private: explicit AnimationCache(QObject* parent = NULL); @@ -54,6 +54,7 @@ class Animation : public Resource { Animation(const Animation& other) : Resource(other), _hfmModel(other._hfmModel) {} Animation(const QUrl& url) : Resource(url) {} + virtual ~Animation() {} QString getType() const override { return "Animation"; } diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index d381cdc872a..0bddf69bb15 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -76,7 +76,7 @@ class AnimationObject : public QObject, protected Scriptable { /// Scriptable wrapper for animation frames. class AnimationFrameObject : public QObject, protected Scriptable { Q_OBJECT - Q_PROPERTY(QVector rotations READ getRotations) + Q_PROPERTY(QVector> rotations READ getRotations) public: @@ -85,7 +85,7 @@ class AnimationFrameObject : public QObject, protected Scriptable { * @function AnimationFrameObject.getRotations * @returns {Quat[]} The joint rotations in the animation frame. */ - Q_INVOKABLE QVector getRotations() const; + Q_INVOKABLE QVector> getRotations() const; }; void registerAnimationTypes(ScriptEngine* engine); diff --git a/libraries/animation/src/Flow.cpp b/libraries/animation/src/Flow.cpp index aaa840ecc04..2ac8296a1fe 100644 --- a/libraries/animation/src/Flow.cpp +++ b/libraries/animation/src/Flow.cpp @@ -12,6 +12,8 @@ #include "Rig.h" #include "AnimSkeleton.h" +#include + const std::map PRESET_FLOW_DATA = { { "hair", FlowPhysicsSettings() }, { "skirt", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) }, { "breast", FlowPhysicsSettings(true, 1.0f, DEFAULT_GRAVITY, 0.65f, 0.8f, 0.45f, 0.01f) } }; diff --git a/libraries/animation/src/Flow.h b/libraries/animation/src/Flow.h index 6235b977578..50f5adb3130 100644 --- a/libraries/animation/src/Flow.h +++ b/libraries/animation/src/Flow.h @@ -18,6 +18,9 @@ #include #include #include + +#include + #include "AnimPose.h" class Rig; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a6b8b282ed4..593e6f1341f 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -38,6 +38,8 @@ #include #endif +#include +#include #include #include #include @@ -57,6 +59,7 @@ #include "AudioClientLogging.h" #include "AudioLogging.h" #include "AudioHelpers.h" +#include "../../../interface/src/scripting/AudioDevices.h" #if defined(Q_OS_ANDROID) #include @@ -85,11 +88,11 @@ using Lock = std::unique_lock; Mutex _deviceMutex; Mutex _recordMutex; -QString defaultAudioDeviceName(QAudio::Mode mode); +QString defaultAudioDeviceName(QAudioDevice::Mode mode); -void AudioClient::setHmdAudioName(QAudio::Mode mode, const QString& name) { +void AudioClient::setHmdAudioName(QAudioDevice::Mode mode, const QString& name) { QWriteLocker lock(&_hmdNameLock); - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Input) { _hmdInputName = name; } else { _hmdOutputName = name; @@ -97,20 +100,27 @@ void AudioClient::setHmdAudioName(QAudio::Mode mode, const QString& name) { } // thread-safe -QList getAvailableDevices(QAudio::Mode mode, const QString& hmdName) { +QList getAvailableDevices(QAudioDevice::Mode mode, const QString& hmdName) { //get hmd device name prior to locking device mutex. in case of shutdown, this thread will be locked and audio client - //cannot properly shut down. + //cannot properly shut down. QString defDeviceName = defaultAudioDeviceName(mode); // NOTE: availableDevices() clobbers the Qt internal device list + QList devices; Lock lock(_deviceMutex); - auto devices = QAudioDeviceInfo::availableDevices(mode); + if (mode == QAudioDevice::Input) { + devices = QMediaDevices::audioInputs(); + } else if (mode == QAudioDevice::Output) { + devices = QMediaDevices::audioOutputs(); + } else if (mode == QAudioDevice::Null) { + Q_ASSERT(false); + } HifiAudioDeviceInfo defaultDesktopDevice; QList newDevices; for (auto& device : devices) { newDevices.push_back(HifiAudioDeviceInfo(device, false, mode)); - if (device.deviceName() == defDeviceName.trimmed()) { + if (device.description() == defDeviceName.trimmed()) { defaultDesktopDevice = HifiAudioDeviceInfo(device, true, mode, HifiAudioDeviceInfo::both); } } @@ -118,7 +128,7 @@ QList getAvailableDevices(QAudio::Mode mode, const QString& if (defaultDesktopDevice.getDevice().isNull()) { if (devices.size() > 0) { qCDebug(audioclient) << __FUNCTION__ << "Default device not found in list:" << defDeviceName - << "Setting Default to: " << devices.first().deviceName(); + << "Setting Default to: " << devices.first().description(); newDevices.push_front(HifiAudioDeviceInfo(devices.first(), true, mode, HifiAudioDeviceInfo::both)); } else { //current audio list is empty for some reason. @@ -131,7 +141,7 @@ QList getAvailableDevices(QAudio::Mode mode, const QString& if (!hmdName.isNull()) { HifiAudioDeviceInfo hmdDevice; foreach(auto device, newDevices) { - if (device.getDevice().deviceName() == hmdName) { + if (device.getDevice().description() == hmdName) { hmdDevice = HifiAudioDeviceInfo(device.getDevice(), true, mode, HifiAudioDeviceInfo::hmd); break; } @@ -161,8 +171,8 @@ void AudioClient::checkDevices() { hmdOutputName = _hmdOutputName; } - auto inputDevices = getAvailableDevices(QAudio::AudioInput, hmdInputName); - auto outputDevices = getAvailableDevices(QAudio::AudioOutput, hmdOutputName); + auto inputDevices = getAvailableDevices(QAudioDevice::Mode::Input, hmdInputName); + auto outputDevices = getAvailableDevices(QAudioDevice::Mode::Output, hmdOutputName); static const QMetaMethod devicesChangedSig= QMetaMethod::fromSignal(&AudioClient::devicesChanged); //only emit once the scripting interface has connected to the signal @@ -170,30 +180,30 @@ void AudioClient::checkDevices() { Lock lock(_deviceMutex); if (inputDevices != _inputDevices) { _inputDevices.swap(inputDevices); - emit devicesChanged(QAudio::AudioInput, _inputDevices); + emit devicesChanged(QAudioDevice::Mode::Input, _inputDevices); } if (outputDevices != _outputDevices) { _outputDevices.swap(outputDevices); - emit devicesChanged(QAudio::AudioOutput, _outputDevices); + emit devicesChanged(QAudioDevice::Mode::Output, _outputDevices); } } } -HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { +HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudioDevice::Mode mode) const { Lock lock(_deviceMutex); - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { return _inputDeviceInfo; } else { return _outputDeviceInfo; } } -QList AudioClient::getAudioDevices(QAudio::Mode mode) const { +QList AudioClient::getAudioDevices(QAudioDevice::Mode mode) const { Lock lock(_deviceMutex); - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { return _inputDevices; } else { return _outputDevices; @@ -323,10 +333,8 @@ AudioClient::AudioClient() { // Set up the desired audio format, since scripting API expects it to be set and audio scripting API // is initialized before audio thread starts. _desiredInputFormat.setSampleRate(AudioConstants::SAMPLE_RATE); - _desiredInputFormat.setSampleSize(16); - _desiredInputFormat.setCodec("audio/pcm"); - _desiredInputFormat.setSampleType(QAudioFormat::SignedInt); - _desiredInputFormat.setByteOrder(QAudioFormat::LittleEndian); + _desiredInputFormat.setSampleFormat(QAudioFormat::Int16); + _desiredInputFormat.setChannelCount(1); _desiredOutputFormat = _desiredInputFormat; @@ -352,15 +360,15 @@ AudioClient::AudioClient() { connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash - defaultAudioDeviceName(QAudio::AudioInput); - defaultAudioDeviceName(QAudio::AudioOutput); + defaultAudioDeviceName(QAudioDevice::Mode::Input); + defaultAudioDeviceName(QAudioDevice::Mode::Output); checkDevices(); // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; connect(_checkDevicesTimer, &QTimer::timeout, this, [=, this] { - QtConcurrent::run(QThreadPool::globalInstance(), [=, this] { + QThreadPool::globalInstance()->start([=, this] { checkDevices(); // On some systems (Ubuntu) checking all the audio devices can take more than 2 seconds. To // avoid consuming all of the thread pool, don't start the check interval until the previous @@ -374,7 +382,7 @@ AudioClient::AudioClient() { // start a thread to detect peak value changes _checkPeakValuesTimer = new QTimer(this); connect(_checkPeakValuesTimer, &QTimer::timeout, this, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); + QThreadPool::globalInstance()->start([this] { checkPeakValues(); }); }); const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50; _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS); @@ -414,7 +422,6 @@ AudioClient::AudioClient() { } AudioClient::~AudioClient() { - stop(); if (_codec && _encoder) { @@ -461,7 +468,7 @@ void AudioClient::setAudioPaused(bool pause) { } } -HifiAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName, const QString& hmdName, bool isHmd=false) { +HifiAudioDeviceInfo getNamedAudioDeviceForMode(QAudioDevice::Mode mode, const QString& deviceName, const QString& hmdName, bool isHmd=false) { HifiAudioDeviceInfo result; foreach (HifiAudioDeviceInfo audioDevice, getAvailableDevices(mode,hmdName)) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { @@ -484,7 +491,7 @@ QString getWinDeviceName(IMMDevice* pEndpoint) { HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); pPropertyStore = nullptr; - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); + deviceName = QString::fromWCharArray(pv.pwszVal); if (!IsWindows8OrGreater()) { // Windows 7 provides only the 31 first characters of the device name. const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31; @@ -518,10 +525,10 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) { #endif -HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode, const QString& hmdName) { +HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudioDevice::Mode mode, const QString& hmdName) { QString deviceName = defaultAudioDeviceName(mode); -#if defined (Q_OS_ANDROID) - if (mode == QAudio::AudioInput) { +#if defined(Q_OS_ANDROID) + if (mode == QAudioDevice::Mode::Input) { Setting::Handle enableAEC(SETTING_AEC_KEY, DEFAULT_AEC_ENABLED); bool aecEnabled = enableAEC.get(); auto audioClient = DependencyManager::get(); @@ -537,12 +544,12 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode, const QString& return getNamedAudioDeviceForMode(mode, deviceName, hmdName); } -QString defaultAudioDeviceName(QAudio::Mode mode) { +QString defaultAudioDeviceName(QAudioDevice::Mode mode) { QString deviceName; #ifdef __APPLE__ QAudioDeviceInfo device; - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { device = QAudioDeviceInfo::defaultInputDevice(); } else { device = QAudioDeviceInfo::defaultOutputDevice(); @@ -561,7 +568,7 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { kAudioObjectPropertyElementMaster }; - if (mode == QAudio::AudioOutput) { + if (mode == QAudioDevice::Mode::Output) { propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; } @@ -588,8 +595,8 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { #endif #ifdef _WIN32 //Check for Windows Vista or higher, IMMDeviceEnumerator doesn't work below that. - if (!IsWindowsVistaOrGreater()) { // lower then vista - if (mode == QAudio::AudioInput) { + if (!IsWindowsVistaOrGreater()) { // lower then vista + if (mode == QAudioDevice::Mode::Input) { WAVEINCAPS wic; // first use WAVE_MAPPER to get the default devices manufacturer ID waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); @@ -598,7 +605,7 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { #if !defined(NDEBUG) qCDebug(audioclient) << "input device:" << wic.szPname; #endif - deviceName = wic.szPname; + deviceName = QString::fromWCharArray(wic.szPname); } else { WAVEOUTCAPS woc; // first use WAVE_MAPPER to get the default devices manufacturer ID @@ -608,7 +615,7 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { #if !defined(NDEBUG) qCDebug(audioclient) << "output device:" << woc.szPname; #endif - deviceName = woc.szPname; + deviceName = QString::fromWCharArray(woc.szPname); } } else { HRESULT hr = S_OK; @@ -616,7 +623,8 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; - hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); + hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudioDevice::Mode::Output ? eRender : eCapture, eMultimedia, + &pEndpoint); if (hr == E_NOTFOUND) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); @@ -630,32 +638,32 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { CoUninitialize(); } -#if !defined(NDEBUG) - qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") - << " [" << deviceName << "] [" << "]"; +#if !defined(NDEBUG) + qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudioDevice::Mode::Output ? "Output" : "Input") + << " [" << deviceName << "] [" << "]"; #endif #endif #ifdef Q_OS_LINUX - if ( mode == QAudio::AudioInput ) { - deviceName = QAudioDeviceInfo::defaultInputDevice().deviceName(); + if (mode == QAudioDevice::Mode::Input) { + deviceName = QMediaDevices::defaultAudioInput().description(); } else { - deviceName = QAudioDeviceInfo::defaultOutputDevice().deviceName(); + deviceName = QMediaDevices::defaultAudioOutput().description(); } #endif - return deviceName; + return deviceName; } -bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { +bool AudioClient::getNamedAudioDeviceForModeExists(QAudioDevice::Mode mode, const QString& deviceName) { QReadLocker readLock(&_hmdNameLock); - QString hmdName = mode == QAudio::AudioInput ? _hmdInputName : _hmdOutputName; + QString hmdName = mode == QAudioDevice::Mode::Input ? _hmdInputName : _hmdOutputName; return (getNamedAudioDeviceForMode(mode, deviceName, hmdName).deviceName() == deviceName); } // attempt to use the native sample rate and channel count -bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) { +bool nativeFormatForAudioDevice(const QAudioDevice& audioDevice, QAudioFormat& audioFormat) { audioFormat = audioDevice.preferredFormat(); @@ -666,10 +674,7 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioForma return false; } - audioFormat.setCodec("audio/pcm"); - audioFormat.setSampleSize(16); - audioFormat.setSampleType(QAudioFormat::SignedInt); - audioFormat.setByteOrder(QAudioFormat::LittleEndian); + audioFormat.setSampleFormat(QAudioFormat::Int16); if (!audioDevice.isFormatSupported(audioFormat)) { qCWarning(audioclient) << "The native format is" << audioFormat << "but isFormatSupported() failed."; @@ -688,7 +693,7 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioForma return true; } -bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, +bool adjustedFormatForAudioDevice(const QAudioDevice& audioDevice, const QAudioFormat& desiredAudioFormat, QAudioFormat& adjustedAudioFormat) { @@ -804,10 +809,8 @@ void AudioClient::start() { // set up the desired audio format _desiredInputFormat.setSampleRate(AudioConstants::SAMPLE_RATE); - _desiredInputFormat.setSampleSize(16); - _desiredInputFormat.setCodec("audio/pcm"); - _desiredInputFormat.setSampleType(QAudioFormat::SignedInt); - _desiredInputFormat.setByteOrder(QAudioFormat::LittleEndian); + _desiredInputFormat.setSampleFormat(QAudioFormat::Int16); + _desiredInputFormat.setChannelCount(1); _desiredOutputFormat = _desiredInputFormat; @@ -823,8 +826,8 @@ void AudioClient::start() { // Input was originally set to HifiAudioDeviceInfo(), but that was causing trouble. //Original comment: initialize input to the dummy device to prevent starves - switchInputToAudioDevice(defaultAudioDeviceForMode(QAudio::AudioInput, QString())); - switchOutputToAudioDevice(defaultAudioDeviceForMode(QAudio::AudioOutput, QString())); + switchInputToAudioDevice(defaultAudioDeviceForMode(QAudioDevice::Mode::Input, QString())); + switchOutputToAudioDevice(defaultAudioDeviceForMode(QAudioDevice::Mode::Output, QString())); #if defined(Q_OS_ANDROID) connect(&_checkInputTimer, &QTimer::timeout, this, &AudioClient::checkInputTimeout); @@ -1030,25 +1033,25 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { } -bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo) { +bool AudioClient::switchAudioDevice(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& deviceInfo) { auto device = deviceInfo; if (deviceInfo.getDevice().isNull()) { qCDebug(audioclient) << __FUNCTION__ << " switching to null device :" - << deviceInfo.deviceName() << " : " << deviceInfo.getDevice().deviceName(); + << deviceInfo.deviceName() << " : " << deviceInfo.getDevice().description(); } - if (mode == QAudio::AudioInput) { + if (mode == QAudioDevice::Mode::Input) { return switchInputToAudioDevice(device); } else { return switchOutputToAudioDevice(device); } } -bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName, bool isHmd) { +bool AudioClient::switchAudioDevice(QAudioDevice::Mode mode, const QString& deviceName, bool isHmd) { QString hmdName; { QReadLocker readLock(&_hmdNameLock); - hmdName = mode == QAudio::AudioInput ? _hmdInputName : _hmdOutputName; + hmdName = mode == QAudioDevice::Mode::Input ? _hmdInputName : _hmdOutputName; } return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName, hmdName, isHmd)); } @@ -1815,7 +1818,9 @@ void AudioClient::setAcousticEchoCancellation(bool enable, bool emitSignal) { bool AudioClient::setIsStereoInput(bool isStereoInput) { bool stereoInputChanged = false; - if (isStereoInput != _isStereoInput && _inputDeviceInfo.getDevice().supportedChannelCounts().contains(2)) { + if (isStereoInput != _isStereoInput + && (_inputDeviceInfo.getDevice().minimumChannelCount() <= 2 + && _inputDeviceInfo.getDevice().maximumChannelCount() >= 2)) { _isStereoInput = isStereoInput; stereoInputChanged = true; @@ -1880,8 +1885,8 @@ void AudioClient::outputFormatChanged() { bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); - qCDebug(audioclient) << __FUNCTION__ << "_inputDeviceInfo: [" << _inputDeviceInfo.deviceName() << ":" << _inputDeviceInfo.getDevice().deviceName() - << "-- inputDeviceInfo:" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().deviceName() << "]"; + qCDebug(audioclient) << __FUNCTION__ << "_inputDeviceInfo: [" << _inputDeviceInfo.deviceName() << ":" << _inputDeviceInfo.getDevice().description() + << "-- inputDeviceInfo:" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().description() << "]"; bool supportedFormat = false; // NOTE: device start() uses the Qt internal device list @@ -1903,7 +1908,7 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice _audioInput = NULL; _numInputCallbackBytes = 0; - _inputDeviceInfo.setDevice(QAudioDeviceInfo()); + _inputDeviceInfo.setDevice(QAudioDevice()); } if (_dummyAudioInput) { @@ -1934,11 +1939,11 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice } if (!inputDeviceInfo.getDevice().isNull()) { - qCDebug(audioclient) << "The audio input device" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().deviceName() << "is available."; + qCDebug(audioclient) << "The audio input device" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().description() << "is available."; //do not update UI that we're changing devices if default or same device _inputDeviceInfo = inputDeviceInfo; - emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo); + emit deviceChanged(QAudioDevice::Mode::Input, _inputDeviceInfo); if (adjustedFormatForAudioDevice(_inputDeviceInfo.getDevice(), _desiredInputFormat, _inputFormat)) { qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; @@ -1949,8 +1954,8 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a resampler for input format to network format."; - assert(_inputFormat.sampleSize() == 16); - assert(_desiredInputFormat.sampleSize() == 16); + assert(_inputFormat.sampleFormat() == QAudioFormat::Int16); + assert(_desiredInputFormat.sampleFormat() == QAudioFormat::Int16); int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1; _inputToNetworkResampler = new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount); @@ -1965,7 +1970,7 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice // if the user wants stereo but this device can't provide then bail if (!_isStereoInput || _inputFormat.channelCount() == 2) { - _audioInput = new QAudioInput(_inputDeviceInfo.getDevice(), _inputFormat, this); + _audioInput = new QAudioSource(_inputDeviceInfo.getDevice(), _inputFormat, this); _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); _audioInput->setBufferSize(_numInputCallbackBytes); // different audio input devices may have different volumes @@ -2000,8 +2005,8 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice // This enables clients without a mic to still receive an audio stream from the mixer. if (!_audioInput) { qCDebug(audioclient) << "Audio input device is not available, using dummy input."; - _inputDeviceInfo.setDevice(QAudioDeviceInfo()); - emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo); + _inputDeviceInfo.setDevice(QAudioDevice()); + emit deviceChanged(QAudioDevice::Mode::Input, _inputDeviceInfo); _inputFormat = _desiredInputFormat; qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; @@ -2116,8 +2121,8 @@ void AudioClient::noteAwakening() { bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); - qCDebug(audioclient) << __FUNCTION__ << "_outputdeviceInfo: [" << _outputDeviceInfo.deviceName() << ":" << _outputDeviceInfo.getDevice().deviceName() - << "-- outputDeviceInfo:" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().deviceName() << "]"; + qCDebug(audioclient) << __FUNCTION__ << "_outputdeviceInfo: [" << _outputDeviceInfo.deviceName() << ":" << _outputDeviceInfo.getDevice().description() + << "-- outputDeviceInfo:" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().description() << "]"; bool supportedFormat = false; // NOTE: device start() uses the Qt internal device list @@ -2156,7 +2161,7 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi delete[] _localOutputMixBuffer; _localOutputMixBuffer = NULL; - _outputDeviceInfo.setDevice(QAudioDeviceInfo()); + _outputDeviceInfo.setDevice(QAudioDevice()); } // cleanup any resamplers @@ -2181,11 +2186,11 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi } if (!outputDeviceInfo.getDevice().isNull()) { - qCDebug(audioclient) << "The audio output device" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().deviceName() << "is available."; + qCDebug(audioclient) << "The audio output device" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().description() << "is available."; //do not update UI that we're changing devices if default or same device _outputDeviceInfo = outputDeviceInfo; - emit deviceChanged(QAudio::AudioOutput, _outputDeviceInfo); + emit deviceChanged(QAudioDevice::Mode::Output, _outputDeviceInfo); if (adjustedFormatForAudioDevice(_outputDeviceInfo.getDevice(), _desiredOutputFormat, _outputFormat)) { qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; @@ -2196,8 +2201,8 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a resampler for network format to output format."; - assert(_desiredOutputFormat.sampleSize() == 16); - assert(_outputFormat.sampleSize() == 16); + assert(_desiredOutputFormat.sampleFormat() == QAudioFormat::Int16); + assert(_outputFormat.sampleFormat() == QAudioFormat::Int16); _networkToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); _localToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); @@ -2209,7 +2214,7 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi outputFormatChanged(); // setup our general output device for audio-mixer audio - _audioOutput = new QAudioOutput(_outputDeviceInfo.getDevice(), _outputFormat, this); + _audioOutput = new QAudioSink(_outputDeviceInfo.getDevice(), _outputFormat, this); int deviceChannelCount = _outputFormat.channelCount(); int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); @@ -2222,8 +2227,10 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi _audioOutput->setBufferSize(requestedSize * 8); #endif - connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); + // QT6TODO: I have no idea what to do about this, QAudioSins has no notify signal in Qt6 + //connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); + // QT6TODO: we could set the buffer size before starting the IO device and sink // start the output device _audioOutputIODevice.start(); _audioOutput->start(&_audioOutputIODevice); @@ -2231,7 +2238,7 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi // initialize mix buffers // restrict device callback to _outputPeriod samples - _outputPeriod = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE; + _outputPeriod = _audioOutput->bufferSize() / AudioConstants::SAMPLE_SIZE / _audioOutput->format().channelCount(); // device callback may exceed reported period, so double it to avoid stutter _outputPeriod *= 2; @@ -2267,7 +2274,7 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi localAudioLock.unlock(); // setup a loopback audio output device - _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo.getDevice(), _outputFormat, this); + _loopbackAudioOutput = new QAudioSink(outputDeviceInfo.getDevice(), _outputFormat, this); _timeSinceLastReceived.start(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 06316efee26..48166fb886b 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -164,19 +166,19 @@ class AudioClient : public AbstractAudioInterface, public Dependency { void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; } - Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); + Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec<3,float,glm::packed_highp> scale); bool outputLocalInjector(const AudioInjectorPointer& injector) override; - HifiAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; - QList getAudioDevices(QAudio::Mode mode) const; + HifiAudioDeviceInfo getActiveAudioDevice(QAudioDevice::Mode mode) const; + QList getAudioDevices(QAudioDevice::Mode mode) const; void enablePeakValues(bool enable) { _enablePeakValues = enable; } bool peakValuesAvailable() const; static const float CALLBACK_ACCELERATOR_RATIO; - bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); + bool getNamedAudioDeviceForModeExists(QAudioDevice::Mode mode, const QString& deviceName); void setRecording(bool isRecording) { _isRecording = isRecording; }; bool getRecording() { return _isRecording; }; @@ -254,9 +256,9 @@ public slots: bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } // calling with a null QAudioDevice will use the system default - bool switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo = HifiAudioDeviceInfo()); - bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName, bool isHmd); - void setHmdAudioName(QAudio::Mode mode, const QString& name); + bool switchAudioDevice(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& deviceInfo = HifiAudioDeviceInfo()); + bool switchAudioDevice(QAudioDevice::Mode mode, const QString& deviceName, bool isHmd); + void setHmdAudioName(QAudioDevice::Mode mode, const QString& name); // Qt opensles plugin is not able to detect when the headset is plugged in void setHeadsetPluggedIn(bool pluggedIn); @@ -293,8 +295,8 @@ public slots: void changeDevice(const HifiAudioDeviceInfo& outputDeviceInfo); - void deviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device); - void devicesChanged(QAudio::Mode mode, const QList& devices); + void deviceChanged(QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device); + void devicesChanged(QAudioDevice::Mode mode, const QList& devices); void peakValueListChanged(const QList peakValueList); void receivedFirstPacket(); @@ -372,19 +374,19 @@ public slots: Gate _gate{ this }; Mutex _injectorsMutex; - QAudioInput* _audioInput{ nullptr }; + QAudioSource* _audioInput{ nullptr }; QTimer* _dummyAudioInput{ nullptr }; QAudioFormat _desiredInputFormat; QAudioFormat _inputFormat; QIODevice* _inputDevice{ nullptr }; int _numInputCallbackBytes{ 0 }; - QAudioOutput* _audioOutput{ nullptr }; + QAudioSink* _audioOutput{ nullptr }; std::atomic _audioOutputInitialized { false }; QAudioFormat _desiredOutputFormat; QAudioFormat _outputFormat; int _outputFrameSize{ 0 }; int _numOutputCallbackBytes{ 0 }; - QAudioOutput* _loopbackAudioOutput{ nullptr }; + QAudioSink* _loopbackAudioOutput{ nullptr }; QIODevice* _loopbackOutputDevice{ nullptr }; AudioRingBuffer _inputRingBuffer{ 0 }; LocalInjectorsStream _localInjectorsStream{ 0 , 1 }; diff --git a/libraries/audio-client/src/AudioFileWav.cpp b/libraries/audio-client/src/AudioFileWav.cpp index 613628883c1..57188bd62da 100644 --- a/libraries/audio-client/src/AudioFileWav.cpp +++ b/libraries/audio-client/src/AudioFileWav.cpp @@ -60,9 +60,9 @@ void AudioFileWav::addHeader(const QAudioFormat& audioFormat) { stream << quint16(1); stream << quint16(audioFormat.channelCount()); stream << quint32(audioFormat.sampleRate()); - stream << quint32(audioFormat.sampleRate() * audioFormat.channelCount() * audioFormat.sampleSize() / 8); // bytes per second - stream << quint16(audioFormat.channelCount() * audioFormat.sampleSize() / 8); // block align - stream << quint16(audioFormat.sampleSize()); // bits Per Sample + stream << quint32(audioFormat.sampleRate() * audioFormat.channelCount() * audioFormat.bytesPerSample()); // bytes per second + stream << quint16(audioFormat.channelCount() * audioFormat.bytesPerSample()); // block align + stream << quint16(audioFormat.bytesPerSample() * 8); // bits Per Sample // Init data chunck stream.writeRawData("data", 4); stream << quint32(0); diff --git a/libraries/audio-client/src/HifiAudioDeviceInfo.cpp b/libraries/audio-client/src/HifiAudioDeviceInfo.cpp index ed3d94f2b81..c3545fde84d 100644 --- a/libraries/audio-client/src/HifiAudioDeviceInfo.cpp +++ b/libraries/audio-client/src/HifiAudioDeviceInfo.cpp @@ -14,7 +14,7 @@ const QString HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME = HIFI_AUDIO_DEVICE_INFO_DEFAULT_DEVICE_NAME; -void HifiAudioDeviceInfo::setDevice(QAudioDeviceInfo devInfo) { +void HifiAudioDeviceInfo::setDevice(QAudioDevice devInfo) { _audioDeviceInfo = devInfo; } @@ -23,7 +23,7 @@ HifiAudioDeviceInfo& HifiAudioDeviceInfo::operator=(const HifiAudioDeviceInfo& o _mode = other.getMode(); _isDefault = other.isDefault(); _deviceType = other.getDeviceType(); - _debugName = other.getDevice().deviceName(); + _debugName = other.getDevice().description(); return *this; } diff --git a/libraries/audio-client/src/HifiAudioDeviceInfo.h b/libraries/audio-client/src/HifiAudioDeviceInfo.h index dee7d20b23a..6560be2c547 100644 --- a/libraries/audio-client/src/HifiAudioDeviceInfo.h +++ b/libraries/audio-client/src/HifiAudioDeviceInfo.h @@ -15,8 +15,9 @@ #include -#include +//#include #include +#include #include #define HIFI_AUDIO_DEVICE_INFO_DEFAULT_DEVICE_NAME "default " @@ -37,20 +38,20 @@ class HifiAudioDeviceInfo : public QObject { _mode = deviceInfo.getMode(); _isDefault = deviceInfo.isDefault(); _deviceType = deviceInfo.getDeviceType(); - _debugName = deviceInfo.getDevice().deviceName(); + _debugName = deviceInfo.getDevice().description(); } - HifiAudioDeviceInfo(QAudioDeviceInfo deviceInfo, bool isDefault, QAudio::Mode mode, DeviceType devType=both) : + HifiAudioDeviceInfo(QAudioDevice deviceInfo, bool isDefault, QAudioDevice::Mode mode, DeviceType devType=both) : _audioDeviceInfo(deviceInfo), _isDefault(isDefault), _mode(mode), _deviceType(devType), - _debugName(deviceInfo.deviceName()) { + _debugName(deviceInfo.description()) { } - void setMode(QAudio::Mode mode) { _mode = mode; } + void setMode(QAudioDevice::Mode mode) { _mode = mode; } void setIsDefault() { _isDefault = true; } - void setDevice(QAudioDeviceInfo devInfo); + void setDevice(QAudioDevice devInfo); QString deviceName() const { #if defined(Q_OS_ANDROID) return _audioDeviceInfo.deviceName(); @@ -58,21 +59,21 @@ class HifiAudioDeviceInfo : public QObject { if (_isDefault) { return DEFAULT_DEVICE_NAME; } else { - return _audioDeviceInfo.deviceName(); + return _audioDeviceInfo.description(); } } - QAudioDeviceInfo getDevice() const { return _audioDeviceInfo; } + QAudioDevice getDevice() const { return _audioDeviceInfo; } bool isDefault() const { return _isDefault; } - QAudio::Mode getMode() const { return _mode; } + QAudioDevice::Mode getMode() const { return _mode; } DeviceType getDeviceType() const { return _deviceType; } HifiAudioDeviceInfo& operator=(const HifiAudioDeviceInfo& other); bool operator==(const HifiAudioDeviceInfo& rhs) const; bool operator!=(const HifiAudioDeviceInfo& rhs) const; private: - QAudioDeviceInfo _audioDeviceInfo; + QAudioDevice _audioDeviceInfo; bool _isDefault { false }; - QAudio::Mode _mode { QAudio::AudioInput }; + QAudioDevice::Mode _mode { QAudioDevice::Input }; DeviceType _deviceType{ both }; QString _debugName; diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h index 1f43e5f45a0..ea1fc8b89ce 100644 --- a/libraries/audio/src/AudioScriptingInterface.h +++ b/libraries/audio/src/AudioScriptingInterface.h @@ -196,7 +196,7 @@ class AudioScriptingInterface : public QObject, public Dependency { * sound.ready.connect(onSoundReady); * } */ - Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); + Q_INVOKABLE ScriptAudioInjector* playSound(std::shared_ptr sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); /*@jsdoc * Starts playing the content of an audio file locally (isn't sent to the audio mixer). This is the same as calling @@ -207,7 +207,7 @@ class AudioScriptingInterface : public QObject, public Dependency { * {@link SoundObject} for supported formats. * @returns {AudioInjector} The audio injector that plays the audio file. */ - Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound); + Q_INVOKABLE ScriptAudioInjector* playSystemSound(std::shared_ptr sound); /*@jsdoc * Sets whether the audio input should be used in stereo. If the audio input doesn't support stereo then setting a value diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index b42609d5769..9d00d292388 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -122,8 +122,8 @@ public slots: protected: // disallow copying of InboundAudioStream objects - InboundAudioStream(const InboundAudioStream&); - InboundAudioStream& operator= (const InboundAudioStream&); + InboundAudioStream(const InboundAudioStream&) = delete; + InboundAudioStream& operator= (const InboundAudioStream&) = delete; /// parses the info between the seq num and the audio data in the network packet and calculates /// how many audio samples this packet contains (used when filling in samples for dropped packets). diff --git a/libraries/audio/src/MixedProcessedAudioStream.cpp b/libraries/audio/src/MixedProcessedAudioStream.cpp index d1312edb956..a94dec10729 100644 --- a/libraries/audio/src/MixedProcessedAudioStream.cpp +++ b/libraries/audio/src/MixedProcessedAudioStream.cpp @@ -56,7 +56,7 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) { emit processSamples(decodedBuffer, outputBuffer); _ringBuffer.writeData(outputBuffer.data(), outputBuffer.size()); - qCDebug(audiostream, "Wrote %d samples to buffer (%d available)", outputBuffer.size() / (int)sizeof(int16_t), getSamplesAvailable()); + qCDebug(audiostream, "Wrote %d samples to buffer (%d available)", (int)outputBuffer.size() / (int)sizeof(int16_t), getSamplesAvailable()); } return 0; } @@ -81,7 +81,7 @@ int MixedProcessedAudioStream::parseAudioData(const QByteArray& packetAfterStrea emit processSamples(decodedBuffer, outputBuffer); _ringBuffer.writeData(outputBuffer.data(), outputBuffer.size()); - qCDebug(audiostream, "Wrote %d samples to buffer (%d available)", outputBuffer.size() / (int)sizeof(int16_t), getSamplesAvailable()); + qCDebug(audiostream, "Wrote %d samples to buffer (%d available)", (int)outputBuffer.size() / (int)sizeof(int16_t), getSamplesAvailable()); return packetAfterStreamProperties.size(); } diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index dcb67014984..83481f9bd41 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -74,13 +74,18 @@ AudioData::AudioData(uint32_t numSamples, uint32_t numChannels, const AudioSampl {} void Sound::downloadFinished(const QByteArray& data) { - if (!_self) { + auto sharedSoundPointer = weak_from_this().lock(); + + if (!sharedSoundPointer) { +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(!_wasDeleted); +#endif soundProcessError(301, "Sound object has gone out of scope"); return; } // this is a QRunnable, will delete itself after it has finished running - auto soundProcessor = new SoundProcessor(_self, data); + auto soundProcessor = new SoundProcessor(sharedSoundPointer, data); connect(soundProcessor, &SoundProcessor::onSuccess, this, &Sound::soundProcessSuccess); connect(soundProcessor, &SoundProcessor::onError, this, &Sound::soundProcessError); QThreadPool::globalInstance()->start(soundProcessor); @@ -102,14 +107,14 @@ void Sound::soundProcessError(int error, QString str) { } -SoundProcessor::SoundProcessor(QWeakPointer sound, QByteArray data) : +SoundProcessor::SoundProcessor(std::weak_ptr sound, QByteArray data) : _sound(sound), _data(data) { } void SoundProcessor::run() { - auto sound = qSharedPointerCast(_sound.lock()); + auto sound = std::dynamic_pointer_cast(_sound.lock()); if (!sound) { emit onError(301, "Sound object has gone out of scope"); return; @@ -440,7 +445,7 @@ bool soundSharedPointerFromScriptValue(const ScriptValue& object, SharedSoundPoi SoundScriptingInterface::SoundScriptingInterface(const SharedSoundPointer& sound) : _sound(sound) { // During shutdown we can sometimes get an empty sound pointer back if (_sound) { - QObject::connect(_sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready); + QObject::connect(_sound.get(), &Sound::ready, this, &SoundScriptingInterface::ready); } } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 878343e722b..124c20a750b 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -67,6 +67,7 @@ class Sound : public Resource { public: Sound(const QUrl& url, bool isStereo = false, bool isAmbisonic = false); Sound(const Sound& other) : Resource(other), _audioData(other._audioData), _numChannels(other._numChannels) {} + virtual ~Sound() {} bool isReady() const { return (bool)_audioData; } @@ -103,7 +104,7 @@ class SoundProcessor : public QObject, public QRunnable { uint32_t sampleRate { 0 }; }; - SoundProcessor(QWeakPointer sound, QByteArray data); + SoundProcessor(std::weak_ptr sound, QByteArray data); virtual void run() override; @@ -119,11 +120,11 @@ class SoundProcessor : public QObject, public QRunnable { void onError(int error, QString str); private: - const QWeakPointer _sound; + const std::weak_ptr _sound; const QByteArray _data; }; -typedef QSharedPointer SharedSoundPointer; +typedef std::shared_ptr SharedSoundPointer; /*@jsdoc * An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}. diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 55a32e72375..a4876733dec 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -30,15 +30,19 @@ SoundCache::SoundCache(QObject* parent) : } SharedSoundPointer SoundCache::getSound(const QUrl& url) { - return getResource(url).staticCast(); + auto sound = std::dynamic_pointer_cast(getResource(url)); + Q_ASSERT(sound); + return sound; } -QSharedPointer SoundCache::createResource(const QUrl& url) { - auto resource = QSharedPointer(new Sound(url), &Resource::deleter); +std::shared_ptr SoundCache::createResource(const QUrl& url) { + auto resource = std::shared_ptr(new Sound(url), Resource::sharedPtrDeleter); resource->setLoadPriorityOperator(this, []() { return SOUNDS_LOADING_PRIORITY; }); return resource; } -QSharedPointer SoundCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new Sound(*resource.staticCast()), &Resource::deleter); +std::shared_ptr SoundCache::createResourceCopy(const std::shared_ptr& resource) { + auto sound = std::dynamic_pointer_cast(resource); + Q_ASSERT(sound); + return std::shared_ptr(new Sound(*sound), Resource::sharedPtrDeleter); } \ No newline at end of file diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h index 973edc40487..f704d9c0921 100644 --- a/libraries/audio/src/SoundCache.h +++ b/libraries/audio/src/SoundCache.h @@ -26,8 +26,8 @@ class SoundCache : public ResourceCache, public Dependency { Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); protected: - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; private: SoundCache(QObject* parent = NULL); diff --git a/libraries/audio/src/SoundCacheScriptingInterface.h b/libraries/audio/src/SoundCacheScriptingInterface.h index 41ad7b69387..628f8bcb578 100644 --- a/libraries/audio/src/SoundCacheScriptingInterface.h +++ b/libraries/audio/src/SoundCacheScriptingInterface.h @@ -60,7 +60,7 @@ class SoundCacheScriptingInterface : public ScriptableResourceCache, public Depe * formats. * @returns {SoundObject} The sound ready for playback. */ - Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); + Q_INVOKABLE std::shared_ptr getSound(const QUrl& url); }; #endif // hifi_SoundCacheScriptingInterface_h diff --git a/libraries/audio/src/flump3dec.cpp b/libraries/audio/src/flump3dec.cpp index 7349cda7e42..79fec0992a9 100644 --- a/libraries/audio/src/flump3dec.cpp +++ b/libraries/audio/src/flump3dec.cpp @@ -101,7 +101,7 @@ * As Fluendo can not assure that any of the activities you undertake do not * infringe any patents or other industrial or intellectual property rights, * Fluendo hereby disclaims any liability for any patent infringement that may be - * claimed to you or to any other person from any legitimate right’s owner, as + * claimed to you or to any other person from any legitimate right's owner, as * stated in MIT license. So it is your responsibility to get information and to * acquire the necessary patent licenses to undertake your activities legally. */ diff --git a/libraries/audio/src/flump3dec.h b/libraries/audio/src/flump3dec.h index 688831da17d..e7c19cea4b3 100644 --- a/libraries/audio/src/flump3dec.h +++ b/libraries/audio/src/flump3dec.h @@ -101,7 +101,7 @@ * As Fluendo can not assure that any of the activities you undertake do not * infringe any patents or other industrial or intellectual property rights, * Fluendo hereby disclaims any liability for any patent infringement that may be - * claimed to you or to any other person from any legitimate right’s owner, as + * claimed to you or to any other person from any legitimate right's owner, as * stated in MIT license. So it is your responsibility to get information and to * acquire the necessary patent licenses to undertake your activities legally. */ diff --git a/libraries/auto-updater/src/AutoUpdater.h b/libraries/auto-updater/src/AutoUpdater.h index c788ac31d10..34350f8943e 100644 --- a/libraries/auto-updater/src/AutoUpdater.h +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 7c1cfcd4403..298b75fe631 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -136,7 +136,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the * registration point of the 3D model. */ - Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: static void setShowAvatars(bool render); @@ -194,9 +194,9 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM }; virtual void indicateLoadingStatus(LoadingStatus loadingStatus) { _loadingStatus = loadingStatus; } - virtual QVector getJointRotations() const override; + virtual QVector> getJointRotations() const override; using AvatarData::getJointRotation; - virtual glm::quat getJointRotation(int index) const override; + virtual glm::qua getJointRotation(int index) const override; virtual QVector getJointTranslations() const override; using AvatarData::getJointTranslation; virtual glm::vec3 getJointTranslation(int index) const override; @@ -213,7 +213,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} index - The joint index. * @returns {Quat} The default rotation of the joint if the joint index is valid, otherwise {@link Quat(0)|Quat.IDENTITY}. */ - Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const; + Q_INVOKABLE virtual glm::qua getDefaultJointRotation(int index) const; /*@jsdoc * Gets the default translation of a joint (in the current avatar) relative to its parent, in model coordinates. @@ -225,7 +225,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @returns {Vec3} The default translation of the joint (in model coordinates) if the joint index is valid, otherwise * {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const; + Q_INVOKABLE virtual glm::vec<3,float,glm::packed_highp> getDefaultJointTranslation(int index) const; /*@jsdoc * Gets the default joint rotations in avatar coordinates. @@ -239,7 +239,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * var defaultHeadRotation = MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(headIndex); * print("Default head rotation: " + JSON.stringify(Quat.safeEulerAngles(defaultHeadRotation))); // Degrees */ - Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const; + Q_INVOKABLE virtual glm::qua getAbsoluteDefaultJointRotationInObjectFrame(int index) const; /*@jsdoc * Gets the default joint translations in avatar coordinates. @@ -253,11 +253,11 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * var defaultHeadTranslation = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(headIndex); * print("Default head translation: " + JSON.stringify(defaultHeadTranslation)); */ - Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const; + Q_INVOKABLE virtual glm::vec<3,float,glm::packed_highp> getAbsoluteDefaultJointTranslationInObjectFrame(int index) const; virtual glm::vec3 getAbsoluteJointScaleInObjectFrame(int index) const override; - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + virtual glm::qua getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; /*@jsdoc @@ -268,7 +268,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {Quat} rotation - The rotation of the joint relative to the avatar. Not used. * @returns {boolean} false. */ - virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } + virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::qua& rotation) override { return false; } /*@jsdoc * Sets the translation of a joint relative to the avatar. @@ -291,7 +291,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Vec3} The position in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ - Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToJointPoint(const glm::vec<3,float,glm::packed_highp>& position, const int jointIndex = -1) const; /*@jsdoc * Transforms a direction in world coordinates to a direction in a joint's coordinates, or avatar coordinates if no joint @@ -301,7 +301,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Vec3} The direction in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ - Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToJointDirection(const glm::vec<3,float,glm::packed_highp>& direction, const int jointIndex = -1) const; /*@jsdoc * Transforms a rotation in world coordinates to a rotation in a joint's coordinates, or avatar coordinates if no joint is @@ -311,7 +311,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Quat} The rotation in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ - Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const; + Q_INVOKABLE glm::qua worldToJointRotation(const glm::qua& rotation, const int jointIndex = -1) const; /*@jsdoc * Transforms a position in a joint's coordinates, or avatar coordinates if no joint is specified, to a position in world @@ -321,7 +321,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Vec3} The position in world coordinates. */ - Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> jointToWorldPoint(const glm::vec<3,float,glm::packed_highp>& position, const int jointIndex = -1) const; /*@jsdoc * Transforms a direction in a joint's coordinates, or avatar coordinates if no joint is specified, to a direction in world @@ -331,7 +331,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Vec3} The direction in world coordinates. */ - Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> jointToWorldDirection(const glm::vec<3,float,glm::packed_highp>& direction, const int jointIndex = -1) const; /*@jsdoc * Transforms a rotation in a joint's coordinates, or avatar coordinates if no joint is specified, to a rotation in world @@ -341,7 +341,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} [jointIndex=-1] - The index of the joint. * @returns {Quat} The rotation in world coordinates. */ - Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; + Q_INVOKABLE glm::qua jointToWorldRotation(const glm::qua& rotation, const int jointIndex = -1) const; Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; @@ -364,7 +364,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * MyAvatar.setSkeletonOffset(Vec3.ZERO); * }, 5000); */ - Q_INVOKABLE void setSkeletonOffset(const glm::vec3& offset); + Q_INVOKABLE void setSkeletonOffset(const glm::vec<3,float,glm::packed_highp>& offset); /*@jsdoc * Gets the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example, @@ -374,7 +374,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @example Report your avatar's current skeleton offset. * print(JSON.stringify(MyAvatar.getSkeletonOffset())); */ - Q_INVOKABLE glm::vec3 getSkeletonOffset() { return _skeletonOffset; } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getSkeletonOffset() { return _skeletonOffset; } virtual glm::vec3 getSkeletonPosition() const; @@ -384,7 +384,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @param {number} index - The index of the joint. * @returns {Vec3} The position of the joint in world coordinates. */ - Q_INVOKABLE glm::vec3 getJointPosition(int index) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getJointPosition(int index) const; /*@jsdoc * Gets the position of a joint in the current avatar. @@ -394,7 +394,7 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @example Report the position of your avatar's hips. * print(JSON.stringify(MyAvatar.getJointPosition("Hips"))); */ - Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getJointPosition(const QString& name) const; /*@jsdoc * Gets the position of the current avatar's neck in world coordinates. @@ -403,21 +403,21 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @example Report the position of your avatar's neck. * print(JSON.stringify(MyAvatar.getNeckPosition())); */ - Q_INVOKABLE glm::vec3 getNeckPosition() const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getNeckPosition() const; /*@jsdoc * Gets the current acceleration of the avatar. * @function MyAvatar.getAcceleration * @returns {Vec3} The current acceleration of the avatar. */ - Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getAcceleration() const { return _acceleration; } /// Scales a world space position vector relative to the avatar position and scale /// \param vector position to be scaled. Will store the result void scaleVectorRelativeToPosition(glm::vec3& positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude(const glm::quat& orientation) override; + virtual void updateAttitude(const glm::qua& orientation) override; // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity @@ -436,10 +436,10 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM * @function MyAvatar.getWorldFeetPosition * @returns {Vec3} The position of the avatar's feet in world coordinates. */ - Q_INVOKABLE glm::vec3 getWorldFeetPosition(); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getWorldFeetPosition(); void setPositionViaScript(const glm::vec3& position) override; - void setOrientationViaScript(const glm::quat& orientation) override; + void setOrientationViaScript(const glm::qua& orientation) override; /*@jsdoc * Gets the ID of the entity or avatar that the avatar is parented to. @@ -486,9 +486,9 @@ class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaM // NOT thread safe, must be called on main thread. glm::vec3 getUncachedLeftPalmPosition() const; - glm::quat getUncachedLeftPalmRotation() const; + glm::qua getUncachedLeftPalmRotation() const; glm::vec3 getUncachedRightPalmPosition() const; - glm::quat getUncachedRightPalmRotation() const; + glm::qua getUncachedRightPalmRotation() const; uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; } void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; } @@ -583,7 +583,7 @@ public slots: * @example Report the position of your avatar's left palm. * print(JSON.stringify(MyAvatar.getLeftPalmPosition())); */ - glm::vec3 getLeftPalmPosition() const; + glm::vec<3,float,glm::packed_highp> getLeftPalmPosition() const; /*@jsdoc * Gets the rotation of the left palm in world coordinates. @@ -592,7 +592,7 @@ public slots: * @example Report the rotation of your avatar's left palm. * print(JSON.stringify(MyAvatar.getLeftPalmRotation())); */ - glm::quat getLeftPalmRotation() const; + glm::qua getLeftPalmRotation() const; /*@jsdoc * Gets the position of the right palm in world coordinates. @@ -601,7 +601,7 @@ public slots: * @example Report the position of your avatar's right palm. * print(JSON.stringify(MyAvatar.getRightPalmPosition())); */ - glm::vec3 getRightPalmPosition() const; + glm::vec<3,float,glm::packed_highp> getRightPalmPosition() const; /*@jsdoc * Get the rotation of the right palm in world coordinates. @@ -610,7 +610,7 @@ public slots: * @example Report the rotation of your avatar's right palm. * print(JSON.stringify(MyAvatar.getRightPalmRotation())); */ - glm::quat getRightPalmRotation() const; + glm::qua getRightPalmRotation() const; /*@jsdoc * @function MyAvatar.setModelURLFinished diff --git a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h index 906d3d438d3..bfcf37943ed 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h @@ -76,7 +76,7 @@ class ScriptAvatar : public ScriptAvatarData { Q_OBJECT - Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> skeletonOffset READ getSkeletonOffset) public: ScriptAvatar(AvatarSharedPointer avatarData); @@ -92,7 +92,7 @@ public slots: * @returns {Quat} The default rotation of the joint if avatar data are available and the joint index is valid, otherwise * {@link Quat(0)|Quat.IDENTITY}. */ - glm::quat getDefaultJointRotation(int index) const; + glm::qua getDefaultJointRotation(int index) const; /*@jsdoc * Gets the default translation of a joint in the avatar relative to its parent, in model coordinates. @@ -104,7 +104,7 @@ public slots: * @returns {Vec3} The default translation of the joint (in model coordinates) if avatar data are available and the joint * index is valid, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - glm::vec3 getDefaultJointTranslation(int index) const; + glm::vec<3,float,glm::packed_highp> getDefaultJointTranslation(int index) const; /*@jsdoc @@ -112,7 +112,7 @@ public slots: * @function ScriptAvatar.getSkeletonOffset * @returns {Vec3} The skeleton offset if avatar data are available, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - glm::vec3 getSkeletonOffset() const; + glm::vec<3,float,glm::packed_highp> getSkeletonOffset() const; /*@jsdoc @@ -122,7 +122,7 @@ public slots: * @returns {Vec3} The position of the joint in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't * available. */ - glm::vec3 getJointPosition(int index) const; + glm::vec<3,float,glm::packed_highp> getJointPosition(int index) const; /*@jsdoc * Gets the position of a joint in the current avatar. @@ -131,7 +131,7 @@ public slots: * @returns {Vec3} The position of the joint in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't * available. */ - glm::vec3 getJointPosition(const QString& name) const; + glm::vec<3,float,glm::packed_highp> getJointPosition(const QString& name) const; /*@jsdoc * Gets the position of the current avatar's neck in world coordinates. @@ -139,7 +139,7 @@ public slots: * @returns {Vec3} The position of the neck in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't * available. */ - glm::vec3 getNeckPosition() const; + glm::vec<3,float,glm::packed_highp> getNeckPosition() const; /*@jsdoc @@ -147,7 +147,7 @@ public slots: * @function ScriptAvatar.getAcceleration * @returns {Vec3} The current acceleration of the avatar, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't available.. */ - glm::vec3 getAcceleration() const; + glm::vec<3,float,glm::packed_highp> getAcceleration() const; /*@jsdoc @@ -191,7 +191,7 @@ public slots: * @returns {Vec3} The position of the left palm in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't * available. */ - glm::vec3 getLeftPalmPosition() const; + glm::vec<3,float,glm::packed_highp> getLeftPalmPosition() const; /*@jsdoc * Gets the rotation of the left palm in world coordinates. @@ -199,7 +199,7 @@ public slots: * @returns {Quat} The rotation of the left palm in world coordinates, or {@link Quat(0)|Quat.IDENTITY} if the avatar data * aren't available. */ - glm::quat getLeftPalmRotation() const; + glm::qua getLeftPalmRotation() const; /*@jsdoc * Gets the position of the right palm in world coordinates. @@ -207,7 +207,7 @@ public slots: * @returns {Vec3} The position of the right palm in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't * available. */ - glm::vec3 getRightPalmPosition() const; + glm::vec<3,float,glm::packed_highp> getRightPalmPosition() const; /*@jsdoc * Gets the rotation of the right palm in world coordinates. @@ -215,7 +215,7 @@ public slots: * @returns {Quat} The rotation of the right palm in world coordinates, or {@link Quat(0)|Quat.IDENTITY} if the avatar data * aren't available. */ - glm::quat getRightPalmRotation() const; + glm::qua getRightPalmRotation() const; private: std::shared_ptr lockAvatar() const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 38efa34f60c..b0fc9ec24d9 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1907,7 +1908,7 @@ QVector AvatarData::getJointRotations() const { if (QThread::currentThread() != thread()) { QVector result; BLOCKING_INVOKE_METHOD(const_cast(this), "getJointRotations", - Q_RETURN_ARG(QVector, result)); + Q_GENERIC_RETURN_ARG(QVector, result)); return result; } QReadLocker readLock(&_jointDataLock); @@ -2661,7 +2662,14 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result, bool useFrameSkeleton) { - QJsonDocument doc = QJsonDocument(QCborValue::fromCbor(frameData).toJsonValue().toObject()); + QCborParserError cborError; + auto cbor = QCborValue::fromCbor(frameData, &cborError); + if (cborError.error != QCborError::NoError) { + // TODO: what to do if this happens? + qCWarning(avatars) << "AvatarData::fromFrame corrupted avatar data cbor: " << QString(frameData.toHex()); + return; + } + QJsonDocument doc = QJsonDocument(cbor.toJsonValue().toObject()); #ifdef WANT_JSON_DEBUG { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 001ddd877a2..57f63022251 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -551,22 +551,22 @@ class AvatarData : public QObject, public SpatiallyNestable { * this property to false to fully control the mouth facial blend shapes via the * {@link Avatar.setBlendshape} method. */ - Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) Q_PROPERTY(float density READ getDensity) - Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) - Q_PROPERTY(glm::quat orientation READ getWorldOrientation WRITE setOrientationViaScript) - Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) + Q_PROPERTY(glm::qua orientation READ getWorldOrientation WRITE setOrientationViaScript) + Q_PROPERTY(glm::qua headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) Q_PROPERTY(float headRoll READ getHeadRoll WRITE setHeadRoll) - Q_PROPERTY(glm::vec3 velocity READ getWorldVelocity WRITE setWorldVelocity) - Q_PROPERTY(glm::vec3 angularVelocity READ getWorldAngularVelocity WRITE setWorldAngularVelocity) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> velocity READ getWorldVelocity WRITE setWorldVelocity) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> angularVelocity READ getWorldAngularVelocity WRITE setWorldAngularVelocity) Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) @@ -582,9 +582,9 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QUuid sessionUUID READ getSessionUUID NOTIFY sessionUUIDChanged) - Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) - Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) - Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> sensorToWorldMatrix READ getSensorToWorldMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> controllerLeftHandMatrix READ getControllerLeftHandMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> controllerRightHandMatrix READ getControllerRightHandMatrix) Q_PROPERTY(float sensorToWorldScale READ getSensorToWorldScale) @@ -612,7 +612,7 @@ class AvatarData : public QObject, public SpatiallyNestable { const QUuid getSessionUUID() const { return getID(); } - glm::vec3 getHandPosition() const; + glm::vec<3,float,glm::packed_highp> getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); typedef enum { @@ -805,7 +805,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE virtual void setJointData(int index, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& translation); /*@jsdoc * Sets a specific joint's rotation relative to its parent. @@ -818,7 +818,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. */ - Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); + Q_INVOKABLE virtual void setJointRotation(int index, const glm::qua& rotation); /*@jsdoc * Sets a specific joint's translation relative to its parent, in model coordinates. @@ -832,7 +832,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @param {number} index - The index of the joint. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ - Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation); + Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec<3,float,glm::packed_highp>& translation); /*@jsdoc * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default @@ -858,7 +858,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. */ - Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; + Q_INVOKABLE virtual glm::qua getJointRotation(int index) const; /*@jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. @@ -869,7 +869,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. */ - Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; + Q_INVOKABLE virtual glm::vec<3,float,glm::packed_highp> getJointTranslation(int index) const; /*@jsdoc * Sets a specific joint's rotation and position relative to its parent, in model coordinates. @@ -884,7 +884,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ - Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE virtual void setJointData(const QString& name, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& translation); /*@jsdoc * Sets a specific joint's rotation relative to its parent. @@ -917,7 +917,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation); + Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::qua& rotation); /*@jsdoc * Sets a specific joint's translation relative to its parent, in model coordinates. @@ -943,7 +943,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation); + Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec<3,float,glm::packed_highp>& translation); /*@jsdoc * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default @@ -983,7 +983,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const; + Q_INVOKABLE virtual glm::qua getJointRotation(const QString& name) const; /*@jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. @@ -998,7 +998,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const; + Q_INVOKABLE virtual glm::vec<3,float,glm::packed_highp> getJointTranslation(const QString& name) const; /*@jsdoc * Gets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. @@ -1010,7 +1010,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace all "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual QVector getJointRotations() const; + Q_INVOKABLE virtual QVector> getJointRotations() const; /*@jsdoc * Gets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in @@ -1021,7 +1021,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the * Avatar API. */ - Q_INVOKABLE virtual QVector getJointTranslations() const; + Q_INVOKABLE virtual QVector> getJointTranslations() const; /*@jsdoc * Sets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. @@ -1059,7 +1059,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ - Q_INVOKABLE virtual void setJointRotations(const QVector& jointRotations); + Q_INVOKABLE virtual void setJointRotations(const QVector>& jointRotations); /*@jsdoc * Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in @@ -1075,7 +1075,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * the same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the * Avatar API. */ - Q_INVOKABLE virtual void setJointTranslations(const QVector& jointTranslations); + Q_INVOKABLE virtual void setJointTranslations(const QVector>& jointTranslations); /*@jsdoc * Clears all joint translations and rotations that have been set by script. This restores all motion from the default @@ -1293,7 +1293,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe - Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + Q_INVOKABLE glm::mat<4,4,float,glm::packed_highp> getSensorToWorldMatrix() const; /*@jsdoc * Gets the scale that transforms dimensions in the user's real world to the avatar's size in the virtual world. @@ -1318,7 +1318,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe - Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; + Q_INVOKABLE glm::mat<4,4,float,glm::packed_highp> getControllerLeftHandMatrix() const; /*@jsdoc * Gets the rotation and translation of the right hand controller relative to the avatar. @@ -1326,7 +1326,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * @returns {Mat4} The rotation and translation of the right hand controller relative to the avatar. */ // thread safe - Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const; + Q_INVOKABLE glm::mat<4,4,float,glm::packed_highp> getControllerRightHandMatrix() const; /*@jsdoc @@ -1496,7 +1496,7 @@ public slots: * @param {number} index - The index of the joint. Not used. * @returns {Quat} Quat.IDENTITY. */ - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + virtual glm::qua getAbsoluteJointRotationInObjectFrame(int index) const override; /*@jsdoc * Gets the translation of a joint relative to the avatar. @@ -1505,7 +1505,7 @@ public slots: * @param {number} index - The index of the joint. Not used. * @returns {Vec3} Vec3.ZERO. */ - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + virtual glm::vec<3,float,glm::packed_highp> getAbsoluteJointTranslationInObjectFrame(int index) const override; /*@jsdoc * Sets the rotation of a joint relative to the avatar. @@ -1515,7 +1515,7 @@ public slots: * @param {Quat} rotation - The rotation of the joint relative to the avatar. Not used. * @returns {boolean} false. */ - virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } + virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::qua& rotation) override { return false; } /*@jsdoc * Sets the translation of a joint relative to the avatar. @@ -1525,7 +1525,7 @@ public slots: * @param {Vec3} translation - The translation of the joint relative to the avatar. Not used. * @returns {boolean} false. */ - virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } + virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec<3,float,glm::packed_highp>& translation) override { return false; } /*@jsdoc * Gets the target scale of the avatar without any restrictions on permissible values imposed by the domain. In contrast, the diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index dcb0604e504..99d546bc10d 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -105,7 +105,7 @@ class AvatarHashMap : public QObject, public Dependency { * var avatars = AvatarList.getAvatarsInRange(Vec3.ZERO, RANGE); * print("Avatars near the origin: " + JSON.stringify(avatars)); */ - Q_INVOKABLE QVector getAvatarsInRange(const glm::vec3& position, float rangeMeters) const; + Q_INVOKABLE QVector getAvatarsInRange(const glm::vec<3,float,glm::packed_highp>& position, float rangeMeters) const; /*@jsdoc * Gets information about an avatar. @@ -179,7 +179,7 @@ public slots: * @returns {boolean} true if there's an avatar within the specified distance of the point, false * if not. */ - bool isAvatarInRange(const glm::vec3 & position, const float range); + bool isAvatarInRange(const glm::vec<3,float,glm::packed_highp> & position, const float range); protected slots: diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 960423a1eec..205e6d43f9f 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -23,22 +23,22 @@ class ScriptAvatarData : public QObject { // // PHYSICAL PROPERTIES: POSITION AND ORIENTATION // - Q_PROPERTY(glm::vec3 position READ getPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> position READ getPosition) Q_PROPERTY(float scale READ getTargetScale) - Q_PROPERTY(glm::vec3 handPosition READ getHandPosition) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> handPosition READ getHandPosition) Q_PROPERTY(float bodyPitch READ getBodyPitch) Q_PROPERTY(float bodyYaw READ getBodyYaw) Q_PROPERTY(float bodyRoll READ getBodyRoll) - Q_PROPERTY(glm::quat orientation READ getOrientation) - Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation) + Q_PROPERTY(glm::qua orientation READ getOrientation) + Q_PROPERTY(glm::qua headOrientation READ getHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw) Q_PROPERTY(float headRoll READ getHeadRoll) // // PHYSICAL PROPERTIES: VELOCITY // - Q_PROPERTY(glm::vec3 velocity READ getVelocity) - Q_PROPERTY(glm::vec3 angularVelocity READ getAngularVelocity) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> velocity READ getVelocity) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> angularVelocity READ getAngularVelocity) // // IDENTIFIER PROPERTIES @@ -64,9 +64,9 @@ class ScriptAvatarData : public QObject { // // MATRIX PROPERTIES // - Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) - Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) - Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> sensorToWorldMatrix READ getSensorToWorldMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> controllerLeftHandMatrix READ getControllerLeftHandMatrix) + Q_PROPERTY(glm::mat<4,4,float,glm::packed_highp> controllerRightHandMatrix READ getControllerRightHandMatrix) Q_PROPERTY(bool hasPriority READ getHasPriority) @@ -82,8 +82,8 @@ class ScriptAvatarData : public QObject { float getBodyPitch() const; float getBodyYaw() const; float getBodyRoll() const; - glm::quat getOrientation() const; - glm::quat getHeadOrientation() const; + glm::qua getOrientation() const; + glm::qua getHeadOrientation() const; float getHeadPitch() const; float getHeadYaw() const; float getHeadRoll() const; @@ -123,7 +123,7 @@ class ScriptAvatarData : public QObject { * @returns {Quat} The rotation of the joint relative to its parent, or {@link Quat(0)|Quat.IDENTITY} if the avatar data * aren't available. */ - Q_INVOKABLE glm::quat getJointRotation(int index) const; + Q_INVOKABLE glm::qua getJointRotation(int index) const; /*@jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. @@ -135,7 +135,7 @@ class ScriptAvatarData : public QObject { * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates, or {@link Vec3(0)|Vec3.ZERO} * if the avatar data aren't available. */ - Q_INVOKABLE glm::vec3 getJointTranslation(int index) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getJointTranslation(int index) const; /*@jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see @@ -145,7 +145,7 @@ class ScriptAvatarData : public QObject { * @returns {Quat} The rotation of the joint relative to its parent, or {@link Quat(0)|Quat.IDENTITY} if the avatar data * aren't available. */ - Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; + Q_INVOKABLE glm::qua getJointRotation(const QString& name) const; /*@jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. @@ -157,7 +157,7 @@ class ScriptAvatarData : public QObject { * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates, or {@link Vec3(0)|Vec3.ZERO} * if the avatar data aren't available. */ - Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const; + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getJointTranslation(const QString& name) const; /*@jsdoc * Gets the rotations of all joints in the avatar. Each joint's rotation is relative to its parent joint. @@ -165,7 +165,7 @@ class ScriptAvatarData : public QObject { * @returns {Quat[]} The rotations of all joints relative to each's parent, or [] if the avatar data aren't * available. The values are in the same order as the array returned by {@link ScriptAvatar.getJointNames}. */ - Q_INVOKABLE QVector getJointRotations() const; + Q_INVOKABLE QVector> getJointRotations() const; /*@jsdoc * Gets the translations of all joints in the avatar. Each joint's translation is relative to its parent joint, in @@ -176,7 +176,7 @@ class ScriptAvatarData : public QObject { * the avatar data aren't available. The values are in the same order as the array returned by * {@link ScriptAvatar.getJointNames}. */ - Q_INVOKABLE QVector getJointTranslations() const; + Q_INVOKABLE QVector> getJointTranslations() const; /*@jsdoc * Checks that the data for a joint are valid. @@ -216,9 +216,9 @@ class ScriptAvatarData : public QObject { // // MATRIX PROPERTIES // - glm::mat4 getSensorToWorldMatrix() const; - glm::mat4 getControllerLeftHandMatrix() const; - glm::mat4 getControllerRightHandMatrix() const; + glm::mat<4,4,float,glm::packed_highp> getSensorToWorldMatrix() const; + glm::mat<4,4,float,glm::packed_highp> getControllerLeftHandMatrix() const; + glm::mat<4,4,float,glm::packed_highp> getControllerRightHandMatrix() const; bool getHasPriority() const; @@ -262,7 +262,7 @@ public slots: * @returns {Quat} The rotation of the joint relative to the avatar, or {@link Quat(0)|Quat.IDENTITY} if the avatar data * aren't available. */ - glm::quat getAbsoluteJointRotationInObjectFrame(int index) const; + glm::qua getAbsoluteJointRotationInObjectFrame(int index) const; /*@jsdoc * Gets the translation of a joint relative to the avatar. @@ -271,7 +271,7 @@ public slots: * @returns {Vec3} The translation of the joint relative to the avatar, or {@link Vec3(0)|Vec3.ZERO} if the avatar data * aren't available. */ - glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const; + glm::vec<3,float,glm::packed_highp> getAbsoluteJointTranslationInObjectFrame(int index) const; protected: std::weak_ptr _avatarData; diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp index e4fdd742561..26786a6a2b6 100644 --- a/libraries/baking/src/JSBaker.cpp +++ b/libraries/baking/src/JSBaker.cpp @@ -274,7 +274,7 @@ bool JSBaker::canOmitNewLine(QChar previousCharacter, QChar nextCharacter) { //Check if character is alphabet, number or one of the following: '_', '$', '\\' or a non-ASCII character bool JSBaker::isAlphanum(QChar c) { return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') - || c == '_' || c == '$' || c == '\\' || c > ASCII_CHARACTERS_UPPER_LIMIT); + || c == '_' || c == '$' || c == '\\' || c > QChar(ASCII_CHARACTERS_UPPER_LIMIT)); } bool JSBaker::isNonAscii(QChar c) { diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 631a10fd11f..669163c6b5a 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -54,7 +54,7 @@ void MaterialBaker::bake() { if (_materialResource->isLoaded()) { processMaterial(); } else { - connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded); + connect(_materialResource.get(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded); } } } @@ -71,7 +71,7 @@ void MaterialBaker::loadMaterial() { if (!_isURL) { qCDebug(material_baking) << "Loading local material" << _materialData; - _materialResource = QSharedPointer::create(); + _materialResource = std::make_shared(); // TODO: add baseURL to allow these to reference relative files next to them _materialResource->parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), QUrl()); } else { @@ -83,7 +83,7 @@ void MaterialBaker::loadMaterial() { if (_materialResource->isLoaded()) { emit originalMaterialLoaded(); } else { - connect(_materialResource.data(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded); + connect(_materialResource.get(), &Resource::finished, this, &MaterialBaker::originalMaterialLoaded); } } else { handleError("Error loading " + _materialData); @@ -139,7 +139,7 @@ void MaterialBaker::processMaterial() { if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) { TextureKey textureKey(textureURL, type); if (!_textureBakers.contains(textureKey)) { - auto baseTextureFileName = _textureFileNamer.createBaseTextureFileName(textureURL.fileName(), type); + auto baseTextureFileName = _textureFileNamer.createBaseTextureFileName(QFileInfo(textureURL.fileName()), type); QSharedPointer textureBaker { new TextureBaker(textureURL, type, _textureOutputDir, baseTextureFileName, content), diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 11b52ba69c8..8d8f4c93029 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -70,7 +70,7 @@ void ModelBaker::setMappingURL(const QUrl& mappingURL) { _mappingURL = mappingURL; } -void ModelBaker::setMapping(const hifi::VariantHash& mapping) { +void ModelBaker::setMapping(const hifi::VariantMultiHash& mapping) { _mapping = mapping; } @@ -227,7 +227,7 @@ void ModelBaker::bakeSourceCopy() { handleError("Could not recognize file type of model file " + _originalOutputModelPath); return; } - hifi::VariantHash serializerMapping = _mapping; + hifi::VariantMultiHash serializerMapping = _mapping; serializerMapping["combineParts"] = true; // set true so that OBJSerializer reads material info from material library serializerMapping["deduplicateIndices"] = true; // Draco compression also deduplicates, but we might as well shave it off to save on some earlier processing (currently FBXSerializer only) hfm::Model::Pointer loadedModel = serializer->read(modelData, serializerMapping, _modelURL); @@ -380,7 +380,7 @@ void ModelBaker::outputUnbakedFST() { outputFSTFilename += FST_EXTENSION; QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename; - hifi::VariantHash outputMapping; + hifi::VariantMultiHash outputMapping; outputMapping[FST_VERSION_FIELD] = FST_VERSION; outputMapping[FILENAME_FIELD] = _modelURL.fileName(); outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change."; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index 4280f8af70b..af33bdf0bf7 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -45,7 +45,7 @@ class ModelBaker : public Baker { void setOutputURLSuffix(const QUrl& urlSuffix); void setMappingURL(const QUrl& mappingURL); - void setMapping(const hifi::VariantHash& mapping); + void setMapping(const hifi::VariantMultiHash& mapping); void initializeOutputDirs(); @@ -74,7 +74,7 @@ public slots: QUrl _modelURL; QUrl _outputURLSuffix; QUrl _mappingURL; - hifi::VariantHash _mapping; + hifi::VariantMultiHash _mapping; QString _bakedOutputDir; QString _originalOutputDir; QString _originalOutputModelPath; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 02423d41486..0ae21a88ee5 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -136,7 +136,7 @@ void TextureBaker::processTexture() { // so we add that to the processed texture before handling it off to be serialized QCryptographicHash hasher(QCryptographicHash::Md5); hasher.addData(_originalTexture); - hasher.addData((const char*)&_textureType, sizeof(_textureType)); + hasher.addData(QByteArrayView((const char*)&_textureType, sizeof(_textureType))); auto hashData = hasher.result(); std::string hash = hashData.toHex().toStdString(); diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index f2a5ca1296e..31030013ca8 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -95,4 +95,6 @@ struct Input { } +Q_DECLARE_METATYPE(controller::Input) + #endif diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 7187dfa3f83..33fb50cfaad 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -51,6 +51,6 @@ namespace controller { }; } -//Q_DECLARE_METATYPE(controller::Pose); +Q_DECLARE_METATYPE(controller::Pose); #endif diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 8db3df5011c..4ae598aebb0 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -52,10 +52,10 @@ namespace controller { public slots: virtual bool isActive() const = 0; - virtual glm::vec3 getAbsTranslation() const = 0; - virtual glm::quat getAbsRotation() const = 0; - virtual glm::vec3 getLocTranslation() const = 0; - virtual glm::quat getLocRotation() const = 0; + virtual glm::vec<3,float,glm::packed_highp> getAbsTranslation() const = 0; + virtual glm::qua getAbsRotation() const = 0; + virtual glm::vec<3,float,glm::packed_highp> getLocTranslation() const = 0; + virtual glm::qua getLocRotation() const = 0; signals: //void spatialEvent(const SpatialEvent& event); @@ -205,7 +205,7 @@ namespace controller { * var RIGHT_HAND = 1; * Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ - Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, uint16_t index = 2) const; + Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, ushort index = 2) const; /*@jsdoc * Triggers a 250ms haptic pulse on connected and enabled devices that have the capability. @@ -216,7 +216,7 @@ namespace controller { * index = 1 is the right hand, and index = 2 is both hands. For other devices, * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulse(float strength, uint16_t index = 2) const; + Q_INVOKABLE bool triggerShortHapticPulse(float strength, ushort index = 2) const; /*@jsdoc * Triggers a haptic pulse on a particular device if connected and enabled and it has the capability. @@ -236,7 +236,7 @@ namespace controller { * Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, - uint16_t index = 2) const; + ushort index = 2) const; /*@jsdoc * Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability. @@ -248,7 +248,7 @@ namespace controller { * index = 1 is the right hand, and index = 2 is both hands. For other devices, * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index = 2) const; + Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, ushort index = 2) const; /*@jsdoc * Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index c20b040a850..1c6b2b99c55 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -232,9 +232,7 @@ namespace controller { } Q_DECLARE_METATYPE(controller::Input::NamedPair) -Q_DECLARE_METATYPE(controller::Pose) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(controller::Input) Q_DECLARE_METATYPE(controller::Action) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(controller::Hand) diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index da3e3ec26be..ef5757c95d7 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -376,7 +376,7 @@ class RouteBuilderProxy : public QObject { * @returns {RouteObject} The RouteObject with the pre-translation applied. */ // No JSDoc example because filter not currently used. - Q_INVOKABLE QObject* translate(glm::vec3 translate); + Q_INVOKABLE QObject* translate(glm::vec<3,float,glm::packed_highp> translate); /*@jsdoc * Filters {@link Pose} route values to have a pre-transform applied. @@ -385,7 +385,7 @@ class RouteBuilderProxy : public QObject { * @returns {RouteObject} The RouteObject with the pre-transform applied. */ // No JSDoc example because filter not currently used. - Q_INVOKABLE QObject* transform(glm::mat4 transform); + Q_INVOKABLE QObject* transform(glm::mat<4,4,float,glm::packed_highp> transform); /*@jsdoc * Filters {@link Pose} route values to have a post-transform applied. @@ -394,7 +394,7 @@ class RouteBuilderProxy : public QObject { * @returns {RouteObject} The RouteObject with the post-transform applied. */ // No JSDoc example because filter not currently used. - Q_INVOKABLE QObject* postTransform(glm::mat4 transform); + Q_INVOKABLE QObject* postTransform(glm::mat<4,4,float,glm::packed_highp> transform); /*@jsdoc * Filters {@link Pose} route values to have a pre-rotation applied. @@ -403,7 +403,7 @@ class RouteBuilderProxy : public QObject { * @returns {RouteObject} The RouteObject with the pre-rotation applied. */ // No JSDoc example because filter not currently used. - Q_INVOKABLE QObject* rotate(glm::quat rotation); + Q_INVOKABLE QObject* rotate(glm::qua rotation); /*@jsdoc * Filters {@link Pose} route values to be smoothed by a low velocity filter. The filter's rotation and translation diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 94aeedcd985..28024da0d78 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index ed16b5875c8..e9a9a1c0cf3 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -175,7 +175,7 @@ QPointF CompositorHelper::getMouseEventPosition(QMouseEvent* event) { QMutexLocker locker(&_reticleLock); return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y); } - return event->localPos(); + return event->position(); } static bool isWindowActive() { diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index a952b802383..fcfef875a44 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -215,7 +215,7 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool visible READ getVisible WRITE setVisible) Q_PROPERTY(float depth READ getDepth WRITE setDepth) Q_PROPERTY(float scale READ getScale WRITE setScale) - Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> maximumPosition READ getMaximumPosition) Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) @@ -258,6 +258,7 @@ class ReticleInterface : public QObject { * @returns {boolean} true if the mouse cursor is pointing at UI in the Interface window in desktop mode or on * the HUD surface in HMD mode, false if it isn't. */ + // QT6TODO: Reticle.isPointingAtSystemOverlay does not work currently Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); } /*@jsdoc @@ -332,7 +333,7 @@ class ReticleInterface : public QObject { * @function Reticle.getMaximumPosition * @returns {Vec2} The maximum reticle coordinates on the display device in desktop mode or the HUD surface in HMD mode. */ - Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + Q_INVOKABLE glm::vec<2,float,glm::packed_highp> getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } private: CompositorHelper* _compositor; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 41555544415..5ddca714d2a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 3ff3441f5cf..aaa7dd4be23 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -9,9 +9,8 @@ #include "StereoDisplayPlugin.h" #include -#include +#include #include -#include #include #include diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index c89c46bd822..0fe4bf3460e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "HTTPConnection.h" #include "EmbeddedWebserverLogging.h" diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 6e36c360c63..b299f1ba596 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -228,7 +228,7 @@ void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() { // Clear the pointer before lambda is run on another thread. _persistentEntitiesScriptManager.reset(); if (scriptManager) { - QtConcurrent::run([manager = scriptManager] { + QThreadPool::globalInstance()->start([manager = scriptManager] { manager->unloadAllEntityScripts(true); manager->stop(); manager->waitTillDoneRunning(); @@ -262,7 +262,7 @@ void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() { // Release the pointer as soon as possible. _nonPersistentEntitiesScriptManager.reset(); if (scriptManager) { - QtConcurrent::run([manager = scriptManager] { + QThreadPool::globalInstance()->start([manager = scriptManager] { manager->unloadAllEntityScripts(true); manager->stop(); manager->waitTillDoneRunning(); @@ -932,7 +932,7 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); auto entityScriptingInterface = DependencyManager::get(); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + PickRay ray = _viewState->computePickRay(event->position().x(), event->position().y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { @@ -966,7 +966,7 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent"); auto entityScriptingInterface = DependencyManager::get(); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + PickRay ray = _viewState->computePickRay(event->position().x(), event->position().y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { @@ -998,7 +998,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent"); auto entityScriptingInterface = DependencyManager::get(); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + PickRay ray = _viewState->computePickRay(event->position().x(), event->position().y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { @@ -1043,7 +1043,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); auto entityScriptingInterface = DependencyManager::get(); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + PickRay ray = _viewState->computePickRay(event->position().x(), event->position().y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { diff --git a/libraries/entities-renderer/src/ModelScriptingInterface.h b/libraries/entities-renderer/src/ModelScriptingInterface.h index cf276e130e3..328ad5351e5 100644 --- a/libraries/entities-renderer/src/ModelScriptingInterface.h +++ b/libraries/entities-renderer/src/ModelScriptingInterface.h @@ -72,7 +72,7 @@ class ModelScriptingInterface : public QObject { * @param {MeshProxy} mesh - The mesh to apply the transform to. * @returns {MeshProxy|boolean} The transformed mesh, if valid. false if an error. */ - Q_INVOKABLE ScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); + Q_INVOKABLE ScriptValue transformMesh(glm::mat<4,4,float,glm::packed_highp> transform, MeshProxy* meshProxy); /*@jsdoc * Creates a new mesh. @@ -82,8 +82,8 @@ class ModelScriptingInterface : public QObject { * @param {MeshFace[]} faces - The faces in the mesh. * @returns {MeshProxy} A new mesh. */ - Q_INVOKABLE ScriptValue newMesh(const QVector& vertices, - const QVector& normals, + Q_INVOKABLE ScriptValue newMesh(const QVector>& vertices, + const QVector>& normals, const QVector& faces); /*@jsdoc diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 1d2b771d4ed..38998d3fe73 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -171,7 +171,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (_networkMaterial->isLoaded()) { onMaterialRequestFinished(!_networkMaterial->isFailed()); } else { - connect(_networkMaterial.data(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) { + connect(_networkMaterial.get(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) { onMaterialRequestFinished(success); }); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 82e11f72a00..a5acebd315e 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -465,7 +465,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + offset) - registrationOffset; } } - shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL() + model->getSnapModelToRegistrationPoint()); + // QT6TODO: I have no idea what implicit conversion from bool to QString did in Qt5 + shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL() + QString::number(model->getSnapModelToRegistrationPoint())); adjustShapeInfoByRegistration(shapeInfo, model->getSnapModelToRegistrationPoint()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { updateModelBounds(); @@ -698,7 +699,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { } } - shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL() + model->getSnapModelToRegistrationPoint()); + shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL() + QString::number(model->getSnapModelToRegistrationPoint())); adjustShapeInfoByRegistration(shapeInfo, model->getSnapModelToRegistrationPoint()); } else { EntityItem::computeShapeInfo(shapeInfo); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 874d6a7057f..2491918a751 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1827,7 +1827,7 @@ void RenderablePolyVoxEntityItem::uncompressVolumeData() { withReadLock([&] { voxelData = _voxelData; }); - QtConcurrent::run([=, this] { + QThreadPool::globalInstance()->start([=, this] { QDataStream reader(voxelData); quint16 voxelXSize, voxelYSize, voxelZSize; reader >> voxelXSize; @@ -1875,7 +1875,7 @@ void RenderablePolyVoxEntityItem::setVoxelsFromData(QByteArray uncompressedData, low += 1; } loop3(ivec3(0), ivec3(voxelXSize, voxelYSize, voxelZSize), [&](const ivec3& v) { - int uncompressedIndex = (v.z * (voxelYSize) * (voxelXSize)) + (v.y * (voxelZSize)) + v.x; + int uncompressedIndex = (v.z * (voxelYSize) * (voxelXSize)) + (v.y * (voxelXSize)) + v.x; result |= setVoxelInternal(v, uncompressedData[uncompressedIndex]); }); @@ -1905,7 +1905,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { qDebug() << "Compressing voxel and sending data packet"; #endif - QtConcurrent::run([voxelXSize, voxelYSize, voxelZSize, entity] { + QThreadPool::globalInstance()->start([voxelXSize, voxelYSize, voxelZSize, entity] { auto polyVoxEntity = std::static_pointer_cast(entity); QByteArray uncompressedData = polyVoxEntity->volDataToArray(voxelXSize, voxelYSize, voxelZSize); @@ -2179,7 +2179,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { mesh = _mesh; }); - QtConcurrent::run([entity, voxelSurfaceStyle, voxelVolumeSize, mesh] { + QThreadPool::globalInstance()->start([entity, voxelSurfaceStyle, voxelVolumeSize, mesh] { auto polyVoxEntity = std::static_pointer_cast(entity); QVector> pointCollection; AABox box; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index d706f46cb11..99cffb41939 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -380,6 +380,11 @@ void entities::TextPayload::render(RenderArgs* args) { if (textColor.a <= 0.0f) { return; } + // QT6TODO: temporary workaround, since I don't know this part of code and how fade works + bool fadingWorkaroundCheck = (!forward && ShapeKey(args->_itemShapeKey).isFaded()) && !args->_shapePipeline; + if (fadingWorkaroundCheck) { + return; + } bool usePrimaryFrustum = args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE || args->_mirrorDepth > 0; transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), textRenderable->_billboardMode, diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 69b59e947b4..af5d9b72f73 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -33,6 +33,7 @@ #include "EntitiesRendererLogging.h" #include #include +#include using namespace render; using namespace render::entities; @@ -53,13 +54,13 @@ const float METERS_TO_INCHES = 39.3701f; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; -static uint8_t YOUTUBE_MAX_FPS = 30; - // Don't allow more than 20 concurrent web views static std::atomic _currentWebCount(0); static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; -static QTouchDevice _touchDevice; +// QT6TODO: Use one shared virtual pointing device, where do we put it though? +static std::shared_ptr _touchDevice; +static std::shared_ptr _mouseDevice; static uint8_t CUSTOM_PIPELINE_NUMBER; // transparent, forward, shadow, fade @@ -139,10 +140,24 @@ WebEntityRenderer::WebEntityRenderer(const EntityItemPointer& entity) : Parent(e static std::once_flag once; std::call_once(once, [&]{ CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(webPipelineFactory); - _touchDevice.setCapabilities(QTouchDevice::Position); - _touchDevice.setType(QTouchDevice::TouchScreen); - _touchDevice.setName("WebEntityRendererTouchDevice"); - _touchDevice.setMaximumTouchPoints(4); + _touchDevice = std::make_shared( + "WebEntityTouchDevice", + 1002, + QInputDevice::DeviceType::TouchScreen, // QT6TODO: test if touchscreen or stylus works better. + QPointingDevice::PointerType::AllPointerTypes, + QInputDevice::Capability::All, + 1, //maxPoints + 2 // buttonCount + ); + _mouseDevice = std::make_shared( + "WebEntityMouseDevice", + 1003, + QInputDevice::DeviceType::Mouse, + QPointingDevice::PointerType::Cursor, + QInputDevice::Capability::All, + 1, //maxPoints + 2 // buttonCount + ); }); _geometryId = DependencyManager::get()->allocateID(); @@ -266,7 +281,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene if (_webSurface) { if (_webSurface->getRootItem()) { - if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) { + if (_sourceURL != newSourceURL) { if (localSafeContext) { ::hifi::scripting::setLocalAccessSafeThread(true); } @@ -275,11 +290,9 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, _useBackground); _webSurface->getRootItem()->setProperty(USER_AGENT_PROPERTY, _userAgent); _webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(_contextPosition)); - _webSurface->setMaxFps((QUrl(newSourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) ? YOUTUBE_MAX_FPS : _maxFPS); + _webSurface->setMaxFps(_maxFPS); ::hifi::scripting::setLocalAccessSafeThread(false); _sourceURL = newSourceURL; - } else if (_contentType != ContentType::HtmlContent) { - _sourceURL = newSourceURL; } { @@ -293,13 +306,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene { auto maxFPS = entity->getMaxFPS(); if (_maxFPS != maxFPS) { - // We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS. - // FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the web entity - if (QUrl(_sourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) { - _webSurface->setMaxFps(YOUTUBE_MAX_FPS); - } else { - _webSurface->setMaxFps(maxFPS); - } + _webSurface->setMaxFps(maxFPS); _maxFPS = maxFPS; } } @@ -473,7 +480,7 @@ void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { if (_webSurface) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverBeginEvent(webEvent, _touchDevice); + _webSurface->hoverBeginEvent(webEvent, *_touchDevice); } }); } @@ -493,7 +500,7 @@ void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { if (_webSurface) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->hoverEndEvent(webEvent, _touchDevice); + _webSurface->hoverEndEvent(webEvent, *_touchDevice); } }); } @@ -515,7 +522,7 @@ void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { void WebEntityRenderer::handlePointerEventAsTouch(const PointerEvent& event) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); - _webSurface->handlePointerEvent(webEvent, _touchDevice); + _webSurface->handlePointerEvent(webEvent, *_touchDevice); } void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) { @@ -552,10 +559,17 @@ void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) { if (type == QEvent::Wheel) { const auto& scroll = event.getScroll() * POINTEREVENT_SCROLL_SENSITIVITY; - QWheelEvent wheelEvent(windowPoint, windowPoint, QPoint(), QPoint(scroll.x, scroll.y), buttons, event.getKeyboardModifiers(), Qt::ScrollPhase::NoScrollPhase, false); + QWheelEvent wheelEvent(windowPoint, windowPoint, QPoint(), QPoint(scroll.x, scroll.y), buttons, event.getKeyboardModifiers(), Qt::ScrollPhase::NoScrollPhase, false, Qt::MouseEventSynthesizedByApplication, _mouseDevice.get()); + wheelEvent.setTimestamp(QDateTime::currentMSecsSinceEpoch()); + QMutableEventPoint::setTimestamp(wheelEvent.point(0), wheelEvent.timestamp()); QCoreApplication::sendEvent(_webSurface->getWindow(), &wheelEvent); } else { - QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers(), _mouseDevice.get()); + //QMouseEvent mouseEvent(type, QPointF(), QPointF(), QPointF(), button, buttons, event.getKeyboardModifiers(), _mouseDevice.get()); + mouseEvent.setTimestamp(QDateTime::currentMSecsSinceEpoch()); + QMutableEventPoint::setTimestamp(mouseEvent.point(0), mouseEvent.timestamp()); + //QMutableEventPoint::setPosition(mouseEvent.point(0), windowPoint); + //QMutableEventPoint::setScenePosition(mouseEvent.point(0), windowPoint); QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 61578acfdac..ad1dc017a34 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -536,7 +536,7 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { if (_ambientTextureURL.isEmpty()) { _pendingAmbientTexture = false; - _ambientTexture.clear(); + _ambientTexture.reset(); _ambientLight->setAmbientMap(nullptr); _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY); @@ -575,7 +575,7 @@ void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) { if (_skyboxTextureURL.isEmpty()) { _pendingSkyboxTexture = false; - _skyboxTexture.clear(); + _skyboxTexture.reset(); editSkybox()->setCubemap(nullptr); } else { diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index c44c21d32ef..223e02c6388 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -217,7 +217,7 @@ glm::vec3 EntityDynamicInterface::extractVec3Argument(QString objectName, QVaria } QVariant resultV = arguments[argumentName]; - if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + if (resultV.typeId() != QMetaType::QVariantMap) { qCDebug(entities) << objectName << "argument" << argumentName << "must be a map"; ok = false; return glm::vec3(0.0f); @@ -266,7 +266,7 @@ glm::quat EntityDynamicInterface::extractQuatArgument(QString objectName, QVaria } QVariant resultV = arguments[argumentName]; - if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + if (resultV.typeId() != QMetaType::QVariantMap) { qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); ok = false; return glm::quat(); diff --git a/libraries/entities/src/EntityItem.cpp.in b/libraries/entities/src/EntityItem.cpp.in index 2abba869857..d6887e94071 100644 --- a/libraries/entities/src/EntityItem.cpp.in +++ b/libraries/entities/src/EntityItem.cpp.in @@ -1831,7 +1831,7 @@ QString EntityItem::actionsToDebugString() { const QUuid id = i.key(); EntityDynamicPointer action = _objectActions[id]; EntityDynamicType actionType = action->getType(); - result += QString("") + actionType + ":" + action->getID().toString() + " "; + result += QString("%1: %2 ").arg(actionType).arg(action->getID().toString()); i++; } return result; diff --git a/libraries/entities/src/EntityItemProperties.cpp.in b/libraries/entities/src/EntityItemProperties.cpp.in index 4af4207dfb0..84eb235f344 100644 --- a/libraries/entities/src/EntityItemProperties.cpp.in +++ b/libraries/entities/src/EntityItemProperties.cpp.in @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -469,7 +470,7 @@ QString EntityItemProperties::getCollisionMaskAsString() const { } void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) { - QVector groups = maskString.splitRef(','); + auto groups = QStringRef(&maskString).split(','); uint16_t mask = 0x0000; for (auto groupName : groups) { mask |= getCollisionGroupAsBitMask(groupName); @@ -996,7 +997,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy int finalizedSize = packetData->getFinalizedSize(); if (finalizedSize <= buffer.size()) { - buffer.replace(0, finalizedSize, finalizedData, finalizedSize); + // QByteArray::replace allows both qsizetype and char* here, so force it to int + buffer.replace(static_cast(0), finalizedSize, finalizedData, finalizedSize); buffer.resize(finalizedSize); } else { qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer."; diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 8b7447cd607..afc5c15d87f 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "Sampler.h" const quint64 UNKNOWN_CREATED_TIME = 0; @@ -287,6 +288,7 @@ typedef QVector qVectorFloat; typedef QVector qVectorQUuid; typedef QVector qVectorQString; typedef QSet qSetQString; +inline QDebug &operator<<(QDebug& debug, const qSetQString& stringSet) { for (const auto &item: stringSet) { debug << item; } return debug; } inline float float_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const ScriptValue& v, bool& isValid) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 219a40901b5..5644d244c24 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -366,7 +366,7 @@ public slots: /// Deliberately not adding jsdoc, only used internally. // FIXME: Deprecate and remove from the API. Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, - bool collisionless, bool grabbable, const glm::vec3& position, const glm::vec3& gravity); + bool collisionless, bool grabbable, const glm::vec<3,float,glm::packed_highp>& position, const glm::vec<3,float,glm::packed_highp>& gravity); /*@jsdoc * Creates a clone of an entity. The clone has the same properties as the original except that: it has a modified @@ -777,7 +777,7 @@ public slots: * print("Closest entity: " + entityID); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; + Q_INVOKABLE QUuid findClosestEntity(const glm::vec<3,float,glm::packed_highp>& center, float radius) const; /*@jsdoc * Finds all domain and avatar entities that intersect a sphere. @@ -793,7 +793,7 @@ public slots: * print("Number of entities within 10m: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; + Q_INVOKABLE QVector findEntities(const glm::vec<3,float,glm::packed_highp>& center, float radius) const; /*@jsdoc * Finds all domain and avatar entities whose axis-aligned boxes intersect a search axis-aligned box. @@ -806,7 +806,7 @@ public slots: * could be found. */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; + Q_INVOKABLE QVector findEntitiesInBox(const glm::vec<3,float,glm::packed_highp>& corner, const glm::vec<3,float,glm::packed_highp>& dimensions) const; /*@jsdoc * Finds all domain and avatar entities whose axis-aligned boxes intersect a search frustum. @@ -840,7 +840,7 @@ public slots: * print("Number of Model entities within 10m: " + entityIDs.length); */ /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; + Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec<3,float,glm::packed_highp>& center, float radius) const; /*@jsdoc * Finds all domain and avatar entities with a particular name that intersect a sphere. @@ -858,7 +858,7 @@ public slots: * var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false); * print("Number of entities with the name Light-Target: " + entityIDs.length); */ - Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, + Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec<3,float,glm::packed_highp>& center, float radius, bool caseSensitiveSearch = false) const; /*@jsdoc @@ -877,7 +877,7 @@ public slots: * var entityIDs = Entities.findEntitiesByTags(["Light-Target"], MyAvatar.position, 10, false); * print("Number of entities with the tag Light-Target: " + entityIDs.length); */ - Q_INVOKABLE QVector findEntitiesByTags(const QVector entityTags, const glm::vec3& center, float radius, + Q_INVOKABLE QVector findEntitiesByTags(const QVector entityTags, const glm::vec<3,float,glm::packed_highp>& center, float radius, bool caseSensitiveSearch = false) const; /*@jsdoc @@ -1062,7 +1062,7 @@ public slots: * Entities.setVoxelSphere(polyVox, position, 0.9, 255); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelSphere(const QUuid& entityID, const glm::vec3& center, float radius, int value); + Q_INVOKABLE bool setVoxelSphere(const QUuid& entityID, const glm::vec<3,float,glm::packed_highp>& center, float radius, int value); /*@jsdoc * Sets the values of all voxels in a capsule-shaped portion of a {@link Entities.EntityProperties-PolyVox|PolyVox} entity. @@ -1086,7 +1086,7 @@ public slots: * Entities.setVoxelCapsule(polyVox, startPosition, endPosition, 0.5, 255); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelCapsule(const QUuid& entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); + Q_INVOKABLE bool setVoxelCapsule(const QUuid& entityID, const glm::vec<3,float,glm::packed_highp>& start, const glm::vec<3,float,glm::packed_highp>& end, float radius, int value); /*@jsdoc * Sets the value of a particular voxel in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity. @@ -1108,7 +1108,7 @@ public slots: * Entities.setVoxel(entity, { x: 0, y: 0, z: 0 }, 0); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxel(const QUuid& entityID, const glm::vec3& position, int value); + Q_INVOKABLE bool setVoxel(const QUuid& entityID, const glm::vec<3,float,glm::packed_highp>& position, int value); /*@jsdoc * Sets the values of all voxels in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity. @@ -1151,7 +1151,7 @@ public slots: * Entities.setVoxelsInCuboid(polyVox, cuboidPosition, cuboidSize, 0); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setVoxelsInCuboid(const QUuid& entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); + Q_INVOKABLE bool setVoxelsInCuboid(const QUuid& entityID, const glm::vec<3,float,glm::packed_highp>& lowPosition, const glm::vec<3,float,glm::packed_highp>& cuboidSize, int value); /*@jsdoc * Converts voxel coordinates in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity to world coordinates. Voxel @@ -1186,7 +1186,7 @@ public slots: * }); */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec<3,float,glm::packed_highp> voxelCoords); /*@jsdoc * Converts world coordinates to voxel coordinates in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity. Voxel @@ -1200,7 +1200,7 @@ public slots: * fractional and outside the entity's bounding box. */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec<3,float,glm::packed_highp> worldCoords); /*@jsdoc * Converts voxel coordinates in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity to local coordinates. Local @@ -1224,7 +1224,7 @@ public slots: * print("Voxel dimensions: " + JSON.stringify(voxelDimensions)); */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec<3,float,glm::packed_highp> voxelCoords); /*@jsdoc * Converts local coordinates to voxel coordinates in a {@link Entities.EntityProperties-PolyVox|PolyVox} entity. Local @@ -1239,7 +1239,7 @@ public slots: * fractional and outside the entity's bounding box. */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> localCoordsToVoxelCoords(const QUuid& entityID, glm::vec<3,float,glm::packed_highp> localCoords); /*@jsdoc * Sets all the points in a {@link Entities.EntityProperties-Line|Line} entity. @@ -1275,7 +1275,7 @@ public slots: * ]); * }, 2000); */ - Q_INVOKABLE bool setAllPoints(const QUuid& entityID, const QVector& points); + Q_INVOKABLE bool setAllPoints(const QUuid& entityID, const QVector>& points); /*@jsdoc * Appends a point to a {@link Entities.EntityProperties-Line|Line} entity. @@ -1307,7 +1307,7 @@ public slots: * Entities.appendPoint(entity, { x: 1, y: 1, z: 0 }); * }, 50); // Wait for the entity to be created. */ - Q_INVOKABLE bool appendPoint(const QUuid& entityID, const glm::vec3& point); + Q_INVOKABLE bool appendPoint(const QUuid& entityID, const glm::vec<3,float,glm::packed_highp>& point); /*@jsdoc * Restart a {@link Entities.EntityProperties-Sound|Sound} entity, locally only. It must also be localOnly. @@ -1414,7 +1414,7 @@ public slots: * {@link Vec3(0)|Vec3.ZERO}. */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex); /*@jsdoc * Gets the index of the parent joint of a joint in a {@link Entities.EntityProperties-Model|Model} entity. @@ -1453,7 +1453,7 @@ public slots: * }, 2000); */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::quat getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex); + Q_INVOKABLE glm::qua getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex); /*@jsdoc * Sets the translation of a joint in a {@link Entities.EntityProperties-Model|Model} entity relative to the entity's @@ -1467,7 +1467,7 @@ public slots: * false. */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex, glm::vec3 translation); + Q_INVOKABLE bool setAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex, glm::vec<3,float,glm::packed_highp> translation); /*@jsdoc * Sets the rotation of a joint in a {@link Entities.EntityProperties-Model|Model} entity relative to the entity's position @@ -1498,7 +1498,7 @@ public slots: * }, 2000); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex, glm::quat rotation); + Q_INVOKABLE bool setAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex, glm::qua rotation); /*@jsdoc @@ -1510,7 +1510,7 @@ public slots: * entity, the entity is loaded, and the joint index is valid; otherwise {@link Vec3(0)|Vec3.ZERO}. */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::vec3 getLocalJointTranslation(const QUuid& entityID, int jointIndex); + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> getLocalJointTranslation(const QUuid& entityID, int jointIndex); /*@jsdoc * Gets the local rotation of a joint in a {@link Entities.EntityProperties-Model|Model} entity. @@ -1536,7 +1536,7 @@ public slots: * }, 2000); */ // FIXME move to a renderable entity interface - Q_INVOKABLE glm::quat getLocalJointRotation(const QUuid& entityID, int jointIndex); + Q_INVOKABLE glm::qua getLocalJointRotation(const QUuid& entityID, int jointIndex); /*@jsdoc * Sets the local translation of a joint in a {@link Entities.EntityProperties-Model|Model} entity. @@ -1549,7 +1549,7 @@ public slots: * false. */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec3 translation); + Q_INVOKABLE bool setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec<3,float,glm::packed_highp> translation); /*@jsdoc * Sets the local rotation of a joint in a {@link Entities.EntityProperties-Model|Model} entity. @@ -1578,7 +1578,7 @@ public slots: * }, 2000); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::quat rotation); + Q_INVOKABLE bool setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::qua rotation); /*@jsdoc @@ -1591,7 +1591,7 @@ public slots: * translations; otherwise false. */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector& translations); + Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector>& translations); /*@jsdoc * Sets the local rotations of joints in a {@link Entities.EntityProperties-Model|Model} entity. @@ -1633,7 +1633,7 @@ public slots: * }, 2000); */ // FIXME move to a renderable entity interface - Q_INVOKABLE bool setLocalJointRotations(const QUuid& entityID, const QVector& rotations); + Q_INVOKABLE bool setLocalJointRotations(const QUuid& entityID, const QVector>& rotations); /*@jsdoc * Sets the local rotations and translations of joints in a {@link Entities.EntityProperties-Model|Model} entity. This is @@ -1649,8 +1649,8 @@ public slots: */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointsData(const QUuid& entityID, - const QVector& rotations, - const QVector& translations); + const QVector>& rotations, + const QVector>& translations); /*@jsdoc @@ -2033,8 +2033,8 @@ public slots: * @param {number} radius - The radius of the capsule. * @returns {boolean} true if the AA box and capsule intersect, otherwise false. */ - Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, - const glm::vec3& start, const glm::vec3& end, float radius); + Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec<3,float,glm::packed_highp>& low, const glm::vec<3,float,glm::packed_highp>& dimensions, + const glm::vec<3,float,glm::packed_highp>& start, const glm::vec<3,float,glm::packed_highp>& end, float radius); /*@jsdoc * Gets the meshes in a {@link Entities.EntityProperties-Model|Model} or {@link Entities.EntityProperties-PolyVox|PolyVox} @@ -2084,7 +2084,7 @@ public slots: * print("Rotation: " + JSON.stringify(Mat4.extractRotation(transform))); // Same as orientation. * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ - Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); + Q_INVOKABLE glm::mat<4,4,float,glm::packed_highp> getEntityTransform(const QUuid& entityID); /*@jsdoc * Gets the object to parent transform, excluding scale, of an entity. @@ -2119,7 +2119,7 @@ public slots: * print("Translation: " + JSON.stringify(Mat4.extractTranslation(transform))); // childTranslation * print("Rotation: " + JSON.stringify(Quat.safeEulerAngles(Mat4.extractRotation(transform)))); // childRotation * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ - Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); + Q_INVOKABLE glm::mat<4,4,float,glm::packed_highp> getEntityLocalTransform(const QUuid& entityID); /*@jsdoc @@ -2156,7 +2156,7 @@ public slots: * localPosition = Entities.getEntityProperties(childEntity, "localPosition").localPosition; * print("Local position: " + JSON.stringify(localPosition)); // The same. */ - Q_INVOKABLE glm::vec3 worldToLocalPosition(glm::vec3 worldPosition, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToLocalPosition(glm::vec<3,float,glm::packed_highp> worldPosition, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a rotation or orientation in world coordinates to rotation in an avatar, entity, or joint's local coordinates. @@ -2168,7 +2168,7 @@ public slots: * @param {boolean} [scalesWithParent=false] - Not used in the calculation. * @returns {Quat} The rotation converted to local coordinates if successful, otherwise {@link Quat(0)|Quat.IDENTITY}. */ - Q_INVOKABLE glm::quat worldToLocalRotation(glm::quat worldRotation, const QUuid& parentID, + Q_INVOKABLE glm::qua worldToLocalRotation(glm::qua worldRotation, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a velocity in world coordinates to a velocity in an avatar, entity, or joint's local coordinates. @@ -2181,7 +2181,7 @@ public slots: * false for the local velocity to be at world scale. * @returns {Vec3} The velocity converted to local coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 worldToLocalVelocity(glm::vec3 worldVelocity, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToLocalVelocity(glm::vec<3,float,glm::packed_highp> worldVelocity, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a Euler angular velocity in world coordinates to an angular velocity in an avatar, entity, or joint's local @@ -2195,7 +2195,7 @@ public slots: * @param {boolean} [scalesWithParent=false] - Not used in the calculation. * @returns {Vec3} The angular velocity converted to local coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 worldToLocalAngularVelocity(glm::vec3 worldAngularVelocity, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToLocalAngularVelocity(glm::vec<3,float,glm::packed_highp> worldAngularVelocity, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts dimensions in world coordinates to dimensions in an avatar or entity's local coordinates. @@ -2207,7 +2207,7 @@ public slots: * false for the local dimensions to be at world scale. * @returns {Vec3} The dimensions converted to local coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 worldToLocalDimensions(glm::vec3 worldDimensions, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> worldToLocalDimensions(glm::vec<3,float,glm::packed_highp> worldDimensions, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a position in an avatar, entity, or joint's local coordinate to a position in world coordinates. @@ -2220,7 +2220,7 @@ public slots: * false if the local dimensions are at world scale. * @returns {Vec3} The position converted to world coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 localToWorldPosition(glm::vec3 localPosition, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> localToWorldPosition(glm::vec<3,float,glm::packed_highp> localPosition, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a rotation or orientation in an avatar, entity, or joint's local coordinate to a rotation in world coordinates. @@ -2232,7 +2232,7 @@ public slots: * @param {boolean} [scalesWithParent= false] - Not used in the calculation. * @returns {Quat} The rotation converted to local coordinates if successful, otherwise {@link Quat(0)|Quat.IDENTITY}. */ - Q_INVOKABLE glm::quat localToWorldRotation(glm::quat localRotation, const QUuid& parentID, + Q_INVOKABLE glm::qua localToWorldRotation(glm::qua localRotation, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a velocity in an avatar, entity, or joint's local coordinate to a velocity in world coordinates. @@ -2245,7 +2245,7 @@ public slots: * false if the local velocity is at world scale. * @returns {Vec3} The velocity converted to world coordinates it successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 localToWorldVelocity(glm::vec3 localVelocity, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> localToWorldVelocity(glm::vec<3,float,glm::packed_highp> localVelocity, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts a Euler angular velocity in an avatar, entity, or joint's local coordinate to an angular velocity in world @@ -2259,7 +2259,7 @@ public slots: * @param {boolean} [scalesWithParent= false] - Not used in the calculation. * @returns {Vec3} The angular velocity converted to world coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 localToWorldAngularVelocity(glm::vec3 localAngularVelocity, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> localToWorldAngularVelocity(glm::vec<3,float,glm::packed_highp> localAngularVelocity, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); /*@jsdoc * Converts dimensions in an avatar or entity's local coordinates to dimensions in world coordinates. @@ -2271,7 +2271,7 @@ public slots: * scale, false if the local dimensions are at world scale. * @returns {Vec3} The dimensions converted to world coordinates if successful, otherwise {@link Vec3(0)|Vec3.ZERO}. */ - Q_INVOKABLE glm::vec3 localToWorldDimensions(glm::vec3 localDimensions, const QUuid& parentID, + Q_INVOKABLE glm::vec<3,float,glm::packed_highp> localToWorldDimensions(glm::vec<3,float,glm::packed_highp> localDimensions, const QUuid& parentID, int parentJointIndex = -1, bool scalesWithParent = false); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 58a79a81265..c21941cee81 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -440,7 +440,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti properties.setParentJointIndexChanged(false); if (wantTerseEditLogging()) { - qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed"; + qCDebug(entities) << (senderNode ? senderNode->getUUID().toString() : "null") << "physical edits suppressed"; } } } @@ -615,7 +615,7 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); entity->forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Avatar) { - child->setParentID(nullptr); + child->setParentID(QUuid()); } }); } @@ -764,7 +764,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) } else { theEntity->forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Avatar) { - child->setParentID(nullptr); + child->setParentID(QUuid()); } }); @@ -2400,7 +2400,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen QJsonValue replaceEntityIDsInJSONHelper(const QJsonValue& jsonValue, std::function getMapped) { if (jsonValue.isString()) { QString stringValue = jsonValue.toString(); - QUuid oldID = stringValue; + QUuid oldID(stringValue); if (!oldID.isNull()) { return QJsonValue(getMapped(oldID).toString()); } @@ -2525,11 +2525,11 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void } } - QUuid oldID = uuidString; + QUuid oldID(uuidString); if (!oldID.isNull()) { uuidString = getMapped(oldID).toString(); } - QUuid oldMaterialName = materialName; + QUuid oldMaterialName(materialName); if (!oldMaterialName.isNull()) { materialName = getMapped(oldMaterialName).toString(); } @@ -2543,7 +2543,7 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void QString imageURL = properties.getImageURL(); if (imageURL.startsWith("{")) { - QUuid oldID = imageURL; + QUuid oldID(imageURL); if (!oldID.isNull()) { properties.setImageURL(getMapped(oldID).toString()); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 99028adfb7a..a88e74a609b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -40,7 +40,7 @@ class NewlyCreatedEntityHook { class SendEntitiesOperationArgs { public: - glm::vec3 root; + glm::vec<3,float,glm::packed_highp> root; QString entityHostType; EntityTree* ourTree; EntityTreePointer otherTree; diff --git a/libraries/entities/src/SoundEntityItem.cpp.in b/libraries/entities/src/SoundEntityItem.cpp.in index a308747bffe..841a2517e6e 100644 --- a/libraries/entities/src/SoundEntityItem.cpp.in +++ b/libraries/entities/src/SoundEntityItem.cpp.in @@ -119,7 +119,7 @@ void SoundEntityItem::update(const quint64& now) { if (_sound->isLoaded()) { updateSound(true); } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + connect(_sound.get(), &Resource::finished, this, [&] { updateSound(true); }); } } } @@ -163,7 +163,7 @@ void SoundEntityItem::setSoundURL(const QString& value) { if (_sound->isLoaded()) { updateSound(true); } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + connect(_sound.get(), &Resource::finished, this, [&] { updateSound(true); }); } } } @@ -334,7 +334,7 @@ void SoundEntityItem::setLocalOnly(bool value) { if (_sound->isLoaded()) { updateSound(true); } else { - connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); + connect(_sound.get(), &Resource::finished, this, [&] { updateSound(true); }); } } } diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt index 2f80489667d..c8cc0cf7963 100644 --- a/libraries/gl/CMakeLists.txt +++ b/libraries/gl/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gl) -setup_hifi_library(Gui Widgets) +setup_hifi_library(Gui Widgets OpenGL) link_hifi_libraries(shared) set(OpenGL_GL_PREFERENCE "GLVND") diff --git a/libraries/gl/src/gl/ContextQt.cpp b/libraries/gl/src/gl/ContextQt.cpp index d8a69d068cb..edd0a49ef44 100644 --- a/libraries/gl/src/gl/ContextQt.cpp +++ b/libraries/gl/src/gl/ContextQt.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Note, `gl::Context is split into two files because a single file cannot include both the GLAD headers +// Note, `gl::Context is split into two files because a single file cannot include both the GLAD headers // and the QOpenGLContext definition headers #include "Context.h" @@ -16,11 +16,7 @@ #include #include "QOpenGLContextWrapper.h" -#ifdef Q_OS_WIN -#include -#endif - -#include +#include #include "GLHelpers.h" diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 7afe6849936..680674a3c9e 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include @@ -112,7 +112,7 @@ GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; static bool setupPixelFormatSimple(HDC hdc) { - // FIXME build the PFD based on the + // FIXME build the PFD based on the static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor @@ -127,7 +127,7 @@ static bool setupPixelFormatSimple(HDC hdc) { 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored - 24, // 24 Bit Z-Buffer (Depth Buffer) + 24, // 24 Bit Z-Buffer (Depth Buffer) 8, // 8 Bit Stencil Buffer 0, // No Auxiliary Buffer PFD_MAIN_PLANE, // Main Drawing Layer @@ -181,7 +181,7 @@ uint16_t gl::getAvailableVersion() { auto backendApi = hifi::properties::getGraphicsAPI(); #if defined(Q_OS_WIN) HINSTANCE hInstance = GetModuleHandle(nullptr); - const auto windowClassName = "OpenGLVersionCheck"; + const wchar_t windowClassName[] = L"OpenGLVersionCheck"; WNDCLASS wc = { }; wc.lpfnWndProc = DefWindowProc; wc.hInstance = hInstance; @@ -190,7 +190,7 @@ uint16_t gl::getAvailableVersion() { using Handle = std::shared_ptr; HWND rawHwnd = CreateWindowEx( - WS_EX_APPWINDOW, // extended style + WS_EX_APPWINDOW, // extended style windowClassName, // class name windowClassName, // title WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CS_OWNDC | WS_POPUP, // style @@ -327,7 +327,7 @@ namespace gl { GLenum error = glGetError(); if (!error) { return false; - } + } switch (error) { case GL_INVALID_ENUM: qCWarning(glLogging) << "GLBackend" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; @@ -356,8 +356,8 @@ namespace gl { bool checkGLErrorDebug(const char* name) { - // Disabling error checking macro on Android debug builds for now, - // as it throws off performance testing, which must be done on + // Disabling error checking macro on Android debug builds for now, + // as it throws off performance testing, which must be done on // Debug builds #if defined(DEBUG) && !defined(Q_OS_ANDROID) return checkGLError(name); diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index 078737443c8..010e6c361af 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -63,7 +63,9 @@ void GLWidget::createContext(QOpenGLContext* shareContext) { _context = new gl::Context(); _context->setWindow(windowHandle()); _context->create(shareContext); - _context->makeCurrent(); + bool isCurrent = _context->makeCurrent(); + Q_ASSERT(isCurrent); + Q_UNUSED(isCurrent); _context->clear(); _context->doneCurrent(); } @@ -135,7 +137,7 @@ bool GLWidget::event(QEvent* event) { return QWidget::event(event); } -bool GLWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) { +bool GLWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) { #ifdef Q_OS_WIN32 MSG* win32message = static_cast(message); switch (win32message->message) { diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h index 0d8b23cd0af..1f907f60a7c 100644 --- a/libraries/gl/src/gl/GLWidget.h +++ b/libraries/gl/src/gl/GLWidget.h @@ -38,7 +38,7 @@ class GLWidget : public QWidget { virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; protected: - virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; + virtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override; virtual bool event(QEvent* event) override; gl::Context* _context { nullptr }; diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index c5b8baa6d44..4d05dbf9271 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include @@ -75,6 +75,10 @@ bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); if (result) { std::call_once(_reportOnce, [] { + // initialise glad before ContextInfo::init uses it, + // otherwise we crash on debug builds because glad + // has its own debug wrappers that need to be set up + gl::initModuleGl(); LOG_GL_CONTEXT_INFO(glLogging, gl::ContextInfo().init()); }); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 842c7abd089..e7b4473b2dc 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -13,26 +13,21 @@ #include -#ifdef Q_OS_WIN -#include -#endif - QOpenGLContextWrapper::Pointer QOpenGLContextWrapper::currentContextWrapper() { return std::make_shared(QOpenGLContext::currentContext()); } - QOpenGLContextWrapper::NativeContextPointer QOpenGLContextWrapper::getNativeContext() const { QOpenGLContextWrapper::NativeContextPointer result; - auto nativeHandle = _context->nativeHandle(); + // QT6TODO: + /*auto nativeHandle = _context->nativeHandle(); if (nativeHandle.canConvert()) { result = std::make_shared(); *result = nativeHandle.value(); - } + }*/ return result; } - uint32_t QOpenGLContextWrapper::currentContextVersion() { QOpenGLContext* context = QOpenGLContext::currentContext(); if (!context) { diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 2d97d35e079..b37ae9a07a3 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -169,7 +169,7 @@ class Buffer : public Resource { /** * Buffer destructor updated statistics. */ - ~Buffer(); + virtual ~Buffer(); /** * @return The size in bytes of data stored in the buffer diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index c731c404c91..955538ffdab 100644 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -230,6 +230,9 @@ Texture::Texture(TextureUsageType usageType) : } Texture::~Texture() { + Q_ASSERT(!wasDeleted); + wasDeleted = true; + _textureCPUCount.decrement(); if (_usageType == TextureUsageType::EXTERNAL) { Texture::ExternalUpdates externalUpdates; @@ -900,10 +903,16 @@ const gpu::TexturePointer TextureSource::getGPUTexture() const { _locked = false; return gpuTexture; } + if (_gpuTexture) { + Q_ASSERT(!_gpuTexture->wasDeleted); + } return _gpuTexture; } void TextureSource::resetTexture(const gpu::TexturePointer& texture) { + if (texture) { + Q_ASSERT(!texture->wasDeleted); + } _gpuTexture = texture; _gpuTextureOperator = nullptr; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index c22781da8cb..74a82c25d5b 100644 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -140,6 +140,7 @@ class Texture : public Resource { static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: + std::atomic wasDeleted{false}; static const uint32_t CUBE_FACE_COUNT { 6 }; static uint32_t getTextureCPUCount(); static Size getTextureCPUMemSize(); @@ -329,8 +330,11 @@ class Texture : public Resource { // After the texture has been created, it should be defined bool isDefined() const { return _defined; } - Texture(TextureUsageType usageType); - ~Texture(); + explicit Texture(TextureUsageType usageType); + // Textures can be only created through `create...` functions + Texture() = delete; + Texture(const Texture &texture) = delete; + virtual ~Texture(); Stamp getStamp() const { return _stamp; } Stamp getDataStamp() const { return _storage->getStamp(); } @@ -605,8 +609,10 @@ class TextureView { TextureView(Texture* newTexture, const Element& element) : _texture(newTexture), _subresource(0), - _element(element) - {}; + _element(element) { + // TODO: this can cause double delete when it's used with a pointer that is already assigned to another shared_ptr. + Q_ASSERT(false); + }; TextureView(const TexturePointer& texture, uint16 subresource, const Element& element) : _texture(texture), _subresource(subresource), @@ -659,7 +665,7 @@ class TextureSource { protected: gpu::TexturePointer _gpuTexture; std::function _gpuTextureOperator { nullptr }; - mutable bool _locked { false }; + mutable std::atomic _locked { false }; QUrl _imageUrl; int _type { 0 }; }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 24486dcd614..b839b1f1606 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -265,7 +265,7 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar if (texCoords0.size()) { mesh->addAttribute(gpu::Stream::TEXCOORD0, buffer_helpers::newFromVector(gpu::Buffer::VertexBuffer, texCoords0, gpu::Format::VEC2F_UV)); } - QVector parts = {{ 0, indices.size(), 0, topology }}; + QVector parts = {graphics::Mesh::Part(0, indices.size(), 0, topology)}; mesh->setPartBuffer(buffer_helpers::newFromVector(gpu::Buffer::IndirectBuffer, parts, gpu::Element::PART_DRAWCALL)); return scriptable::make_scriptowned(mesh, nullptr); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 2412a17d1ac..6e452c91589 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -154,6 +154,7 @@ namespace scriptable { ScriptValue scriptableMaterialToScriptValue(ScriptEngine* engine, const scriptable::ScriptableMaterial &material); }; -Q_DECLARE_METATYPE(NestableType) +// QT6TODO +//Q_DECLARE_METATYPE(NestableType) #endif // hifi_GraphicsScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp index 632303ebc7b..1e00f424ba8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp @@ -27,7 +27,7 @@ QVariant toVariant(const glm::mat4& mat4) { floats.resize(16); memcpy(floats.data(), &mat4, sizeof(glm::mat4)); QVariant v; - v.setValue>(floats); + v.setValue>(std::move(floats)); return v; }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 922e7b79738..fe96341cd50 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -133,7 +133,7 @@ namespace scriptable { * origin it is considered to be "nearby". * @returns {number[]} The indices of nearby vertices. */ - QVector findNearbyVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + QVector findNearbyVertexIndices(const glm::vec<3,float,glm::packed_highp>& origin, float epsilon = 1e-6) const; /*@jsdoc * Adds an attribute for all vertices. diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h index cb06b397107..3ff28c3d222 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h @@ -112,7 +112,7 @@ namespace scriptable { * origin it is considered to be "nearby". * @returns {number[]} The indices of nearby vertices. */ - QVector findNearbyPartVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + QVector findNearbyPartVertexIndices(const glm::vec<3,float,glm::packed_highp>& origin, float epsilon = 1e-6) const; /*@jsdoc * Gets the value of an attribute for all vertices in the whole mesh (i.e., parent and mesh parts). @@ -174,7 +174,7 @@ namespace scriptable { * @param {Vec3} translation - The translation to apply, in model coordinates. * @returns {Graphics.MeshExtents} The rseulting mesh extents, in model coordinates. */ - QVariantMap translate(const glm::vec3& translation); + QVariantMap translate(const glm::vec<3,float,glm::packed_highp>& translation); /*@jsdoc * Scales the mesh part. @@ -183,7 +183,7 @@ namespace scriptable { * @param {Vec3} [origin] - The origin to scale about. If not specified, the center of the mesh part is used. * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. */ - QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap scale(const glm::vec<3,float,glm::packed_highp>& scale, const glm::vec<3,float,glm::packed_highp>& origin = glm::vec<3,float,glm::packed_highp>(NAN)); /*@jsdoc * Rotates the mesh part, using Euler angles. @@ -193,7 +193,7 @@ namespace scriptable { *

Warning: Currently doesn't work as expected.

* @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. */ - QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotateDegrees(const glm::vec<3,float,glm::packed_highp>& eulerAngles, const glm::vec<3,float,glm::packed_highp>& origin = glm::vec<3,float,glm::packed_highp>(NAN)); /*@jsdoc * Rotates the mesh part, using a quaternion. @@ -203,7 +203,7 @@ namespace scriptable { *

Warning: Currently doesn't work as expected.

* @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. */ - QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotate(const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& origin = glm::vec<3,float,glm::packed_highp>(NAN)); /*@jsdoc * Scales, rotates, and translates the mesh. @@ -211,7 +211,7 @@ namespace scriptable { * @param {Mat4} transform - The scale, rotate, and translate transform to apply. * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. */ - QVariantMap transform(const glm::mat4& transform); + QVariantMap transform(const glm::mat<4,4,float,glm::packed_highp>& transform); // @borrows jsdoc from GraphicsMesh. glm::uint32 addAttribute(const QString& attributeName, const QVariant& defaultValue = QVariant()); @@ -339,5 +339,6 @@ namespace scriptable { }; } -Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) -Q_DECLARE_METATYPE(QVector) +// QT6TODO: +//Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) +//Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 9f96f776ef7..1a3de45c5fd 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -179,7 +179,8 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint parametricRimFresnelPower = material->getParametricRimFresnelPower(); parametricRimLift = material->getParametricRimLift(); rimLightingMix = material->getRimLightingMix(); - outlineWidthMode = material->getOutlineWidthMode(); + // QT6TODO: something is wrong there, isn't outlineWidthMode supposed to be a string like "worldCoordinates" or "screenCoordinates" + outlineWidthMode = QString::number(material->getOutlineWidthMode()); outlineWidth = material->getOutlineWidth(); outline = material->getOutline(); uvAnimationScrollXSpeed = material->getUVAnimationScrollXSpeed(); diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 1a130672558..74738cff260 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -47,7 +47,7 @@ namespace buffer_helpers { template const T glmVecFromVariant(const QVariant& v) { - auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; + auto isMap = v.typeId() == QMetaType::QVariantMap; static const auto len = T().length(); const auto& components = isMap ? XYZW : ZERO123; T result; diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 6f5b60a703d..6d9113625e1 100644 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -246,22 +246,25 @@ bool Material::resetOpacityMap() const { _key.setOpacityMaskMap(false); _key.setTranslucentMap(false); - const auto& textureMap = getTextureMap(MaterialKey::ALBEDO_MAP); - if (textureMap && - textureMap->useAlphaChannel() && - textureMap->isDefined() && - textureMap->getTextureView().isValid()) { - - auto usage = textureMap->getTextureView()._texture->getUsage(); - if (usage.isAlpha()) { - if (usage.isAlphaMask()) { - // Texture has alpha, but it is just a mask - _key.setOpacityMaskMap(true); - _key.setTranslucentMap(false); - } else { - // Texture has alpha, it is a true translucency channel - _key.setOpacityMaskMap(false); - _key.setTranslucentMap(true); + const auto textureMap = getTextureMap(MaterialKey::ALBEDO_MAP); + if (textureMap) { + auto textureView = textureMap->getTextureView(); + if (textureView && + textureMap->useAlphaChannel() && + textureMap->isDefined() && + textureView.isValid()) { + + auto usage = textureView._texture->getUsage(); + if (usage.isAlpha()) { + if (usage.isAlphaMask()) { + // Texture has alpha, but it is just a mask + _key.setOpacityMaskMap(true); + _key.setTranslucentMap(false); + } else { + // Texture has alpha, it is a true translucency channel + _key.setOpacityMaskMap(false); + _key.setTranslucentMap(true); + } } } } diff --git a/libraries/hfm/src/hfm/HFMSerializer.h b/libraries/hfm/src/hfm/HFMSerializer.h index d0be588d602..c99d021977e 100644 --- a/libraries/hfm/src/hfm/HFMSerializer.h +++ b/libraries/hfm/src/hfm/HFMSerializer.h @@ -37,7 +37,7 @@ class Serializer { virtual MediaType getMediaType() const = 0; virtual std::unique_ptr getFactory() const = 0; - virtual Model::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) = 0; + virtual Model::Pointer read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url = hifi::URL()) = 0; }; }; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index b6513340a30..e2f29f79d4d 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -204,7 +204,7 @@ glm::vec2 evalAverageTouchPoints(const QList& points) { glm::vec2 averagePoint(0.0f); if (points.count() > 0) { for (auto& point : points) { - averagePoint += glm::vec2(point.pos().x(), point.pos().y()); + averagePoint += glm::vec2(point.position().x(), point.position().y()); } averagePoint /= (float)(points.count()); } @@ -244,8 +244,8 @@ void KeyboardMouseDevice::touchGestureEvent(const QGestureEvent* event) { void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) { if (_enableTouch) { - _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); - _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _isTouching = event->touchPointStates().testFlag(QEventPoint::State::Pressed); + _lastTouch = evalAverageTouchPoints(event->points()); _lastTouchTime = _clock.now(); } } @@ -253,14 +253,14 @@ void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) { void KeyboardMouseDevice::touchEndEvent(const QTouchEvent* event) { if (_enableTouch) { _isTouching = false; - _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _lastTouch = evalAverageTouchPoints(event->points()); _lastTouchTime = _clock.now(); } } void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { if (_enableTouch) { - auto currentPos = evalAverageTouchPoints(event->touchPoints()); + auto currentPos = evalAverageTouchPoints(event->points()); _lastTouchTime = _clock.now(); if (!_isTouching) { diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp index 807d7f0ef92..29ca9520fbc 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp @@ -24,8 +24,8 @@ const char* TouchscreenDevice::NAME = "Touchscreen"; bool TouchscreenDevice::isSupported() const { - for (auto touchDevice : QTouchDevice::devices()) { - if (touchDevice->type() == QTouchDevice::TouchScreen) { + for (auto touchDevice : QInputDevice::devices()) { + if (touchDevice->type() == QInputDevice::DeviceType::TouchScreen) { return true; } } @@ -73,15 +73,17 @@ void TouchscreenDevice::InputDevice::focusOutEvent() { } void TouchscreenDevice::touchBeginEvent(const QTouchEvent* event) { - const QTouchEvent::TouchPoint& point = event->touchPoints().at(0); - _firstTouchVec = glm::vec2(point.pos().x(), point.pos().y()); + const QTouchEvent::TouchPoint& point = event->points().at(0); + _firstTouchVec = glm::vec2(point.position().x(), point.position().y()); KeyboardMouseDevice::enableTouch(false); - QScreen* eventScreen = event->window()->screen(); + // QT6TODO: I'm not sure how to do this part yet + /*QScreen* eventScreen = event->window()->screen(); + QScreen* eventScreen = event->tagret()window()->screen(); if (_screenDPI != eventScreen->physicalDotsPerInch()) { _screenDPIScale.x = (float)eventScreen->physicalDotsPerInchX(); _screenDPIScale.y = (float)eventScreen->physicalDotsPerInchY(); _screenDPI = eventScreen->physicalDotsPerInch(); - } + }*/ } void TouchscreenDevice::touchEndEvent(const QTouchEvent* event) { @@ -90,14 +92,15 @@ void TouchscreenDevice::touchEndEvent(const QTouchEvent* event) { } void TouchscreenDevice::touchUpdateEvent(const QTouchEvent* event) { - const QTouchEvent::TouchPoint& point = event->touchPoints().at(0); - _currentTouchVec = glm::vec2(point.pos().x(), point.pos().y()); - _touchPointCount = event->touchPoints().count(); + const QTouchEvent::TouchPoint& point = event->points().at(0); + _currentTouchVec = glm::vec2(point.position().x(), point.position().y()); + _touchPointCount = static_cast(event->points().count()); } void TouchscreenDevice::touchGestureEvent(const QGestureEvent* event) { if (QGesture* gesture = event->gesture(Qt::PinchGesture)) { - QPinchGesture* pinch = static_cast(gesture); + QPinchGesture* pinch = dynamic_cast(gesture); + Q_ASSERT(pinch); _pinchScale = pinch->totalScaleFactor(); } } diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h index c0cca200d75..eb024247707 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h @@ -14,7 +14,6 @@ #include #include "InputPlugin.h" -#include class QTouchEvent; class QGestureEvent; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 6ba2d0eeea0..12069042458 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -27,8 +27,8 @@ const char* TouchscreenVirtualPadDevice::NAME = "TouchscreenVirtualPad"; bool TouchscreenVirtualPadDevice::isSupported() const { - for (auto touchDevice : QTouchDevice::devices()) { - if (touchDevice->type() == QTouchDevice::TouchScreen) { + for (const auto &touchDevice : QInputDevice::devices()) { + if (touchDevice->type() == QInputDevice::DeviceType::TouchScreen) { return true; } } @@ -211,14 +211,15 @@ void TouchscreenVirtualPadDevice::InputDevice::focusOutEvent() { void TouchscreenVirtualPadDevice::debugPoints(const QTouchEvent* event, QString who) { // convert the touch points into an average - const QList& tPoints = event->touchPoints(); + const QList& tPoints = event->points(); QVector points; int touchPoints = tPoints.count(); for (int i = 0; i < touchPoints; ++i) { - glm::vec2 thisPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); + glm::vec2 thisPoint(tPoints[i].position().x(), tPoints[i].position().y()); points << thisPoint; } - QScreen* eventScreen = event->window()->screen(); + // QT6TODO: I have no idea how to do this part yet + /*QScreen* eventScreen = event->window()->screen(); int midScreenX = eventScreen->availableSize().width()/2; int lefties = 0; int righties = 0; @@ -230,7 +231,7 @@ void TouchscreenVirtualPadDevice::debugPoints(const QTouchEvent* event, QString } else { righties++; } - } + }*/ } void TouchscreenVirtualPadDevice::touchBeginEvent(const QTouchEvent* event) { @@ -287,9 +288,9 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { viewTouchEnd(); return; } - _touchPointCount = event->touchPoints().count(); + _touchPointCount = static_cast(event->points().count()); - const QList& tPoints = event->touchPoints(); + const QList& tPoints = event->points(); bool moveTouchFound = false; bool viewTouchFound = false; @@ -303,8 +304,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { std::map unusedTouchesInEvent; for (int i = 0; i < _touchPointCount; ++i) { - thisPoint.x = tPoints[i].pos().x(); - thisPoint.y = tPoints[i].pos().y(); + thisPoint.x = tPoints[i].position().x(); + thisPoint.y = tPoints[i].position().y(); thisPointId = tPoints[i].id(); if (!moveTouchFound && _moveHasValidTouch && _moveCurrentTouchId == thisPointId) { @@ -357,8 +358,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxMoveStartingPointCandidate != -1) { _moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id(); _unusedTouches.erase(_moveCurrentTouchId); - thisPoint.x = tPoints[idxMoveStartingPointCandidate].pos().x(); - thisPoint.y = tPoints[idxMoveStartingPointCandidate].pos().y(); + thisPoint.x = tPoints[idxMoveStartingPointCandidate].position().x(); + thisPoint.y = tPoints[idxMoveStartingPointCandidate].position().y(); moveTouchBegin(thisPoint); } else { moveTouchEnd(); @@ -368,8 +369,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxViewStartingPointCandidate != -1) { _viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id(); _unusedTouches.erase(_viewCurrentTouchId); - thisPoint.x = tPoints[idxViewStartingPointCandidate].pos().x(); - thisPoint.y = tPoints[idxViewStartingPointCandidate].pos().y(); + thisPoint.x = tPoints[idxViewStartingPointCandidate].position().x(); + thisPoint.y = tPoints[idxViewStartingPointCandidate].position().y(); viewTouchBegin(thisPoint); } else { viewTouchEnd(); @@ -592,8 +593,8 @@ void TouchscreenVirtualPadDevice::TouchscreenButtonsManager::processBeginOrEnd( if (button._candidatePointIdx != -1) { button.currentTouchId = tPoints[button._candidatePointIdx].id(); globalUnusedTouches.erase(button.currentTouchId); - thisPoint.x = tPoints[button._candidatePointIdx].pos().x(); - thisPoint.y = tPoints[button._candidatePointIdx].pos().y(); + thisPoint.x = tPoints[button._candidatePointIdx].position().x(); + thisPoint.y = tPoints[button._candidatePointIdx].position().y(); button.touchBegin(thisPoint); } else { if (button.hasValidTouch) { diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h index 1fa46f8a247..29c09af0372 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h @@ -14,7 +14,6 @@ #include #include "InputPlugin.h" -#include #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) #include #else diff --git a/libraries/material-networking/src/material-networking/ShaderCache.cpp b/libraries/material-networking/src/material-networking/ShaderCache.cpp index 0b87b74ae3c..24ceb14af34 100644 --- a/libraries/material-networking/src/material-networking/ShaderCache.cpp +++ b/libraries/material-networking/src/material-networking/ShaderCache.cpp @@ -21,13 +21,17 @@ ShaderCache& ShaderCache::instance() { } NetworkShaderPointer ShaderCache::getShader(const QUrl& url) { - return ResourceCache::getResource(url).staticCast(); + auto shader = std::dynamic_pointer_cast(ResourceCache::getResource(url)); + Q_ASSERT(shader); + return shader; } -QSharedPointer ShaderCache::createResource(const QUrl& url) { - return QSharedPointer(new NetworkShader(url), &Resource::deleter); +std::shared_ptr ShaderCache::createResource(const QUrl& url) { + return std::shared_ptr(new NetworkShader(url), Resource::sharedPtrDeleter); } -QSharedPointer ShaderCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new NetworkShader(*resource.staticCast()), &Resource::deleter); +std::shared_ptr ShaderCache::createResourceCopy(const std::shared_ptr& resource) { + auto shader = std::dynamic_pointer_cast(resource); + Q_ASSERT(shader); + return std::shared_ptr(new NetworkShader(*shader), Resource::sharedPtrDeleter); } diff --git a/libraries/material-networking/src/material-networking/ShaderCache.h b/libraries/material-networking/src/material-networking/ShaderCache.h index 5129a7d8b40..a4bbaa2d31e 100644 --- a/libraries/material-networking/src/material-networking/ShaderCache.h +++ b/libraries/material-networking/src/material-networking/ShaderCache.h @@ -16,6 +16,7 @@ class NetworkShader : public Resource { public: NetworkShader(const QUrl& url); NetworkShader(const NetworkShader& other) : Resource(other), _source(other._source) {} + virtual ~NetworkShader() {} QString getType() const override { return "NetworkShader"; } @@ -24,7 +25,7 @@ class NetworkShader : public Resource { QString _source; }; -using NetworkShaderPointer = QSharedPointer; +using NetworkShaderPointer = std::shared_ptr; class ShaderCache : public ResourceCache { public: @@ -33,8 +34,8 @@ class ShaderCache : public ResourceCache { NetworkShaderPointer getShader(const QUrl& url); protected: - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; }; #endif diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 1b18c73e2a2..389c9f8d386 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -284,7 +284,10 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs modifiedUrl.setQuery(query.toString()); } TextureExtra extra = { type, content, maxNumPixels, sourceChannel }; - return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash()(extra)).staticCast(); + + auto texture = std::dynamic_pointer_cast(ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash()(extra))); + Q_ASSERT(texture); + return texture; } std::pair TextureCache::getTextureByHash(const std::string& hash) { @@ -376,15 +379,17 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false)); } -QSharedPointer TextureCache::createResource(const QUrl& url) { - return QSharedPointer(new NetworkTexture(url), &Resource::deleter); +std::shared_ptr TextureCache::createResource(const QUrl& url) { + return std::shared_ptr(new NetworkTexture(url), Resource::sharedPtrDeleter); } -QSharedPointer TextureCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new NetworkTexture(*resource.staticCast()), &Resource::deleter); +std::shared_ptr TextureCache::createResourceCopy(const std::shared_ptr& resource) { + auto texture = std::dynamic_pointer_cast(resource); + Q_ASSERT(texture); + return std::shared_ptr(new NetworkTexture(*texture), Resource::sharedPtrDeleter); } -int networkTexturePointerMetaTypeId = qRegisterMetaType>(); +int networkTexturePointerMetaTypeId = qRegisterMetaType>(); NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) : Resource(url), @@ -478,6 +483,8 @@ void NetworkTexture::setExtra(void* extra) { void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { + auto self = weak_from_this().lock(); + // Passing ownership _textureSource->resetTexture(texture); @@ -486,19 +493,42 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); - finishedLoading(true); + if (!_isScheduledForDeletion) { + finishedLoading(true); + } } else { _width = _height = 0; - finishedLoading(false); + if (!_isScheduledForDeletion) { + finishedLoading(false); + } + } + + if (_isScheduledForDeletion) { + return; + } + + if (!self) { + // We need to make sure that texture was just added to unused pool and wasn't deleted yet. +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(!_wasDeleted); +#endif + return; + //TODO: what to do when texture pointer has expired? } - emit networkTextureCreated(qWeakPointerCast (_self)); + auto thisTexture = std::dynamic_pointer_cast(self); + Q_ASSERT(thisTexture); + emit networkTextureCreated(thisTexture); } void NetworkTexture::setImageOperator(std::function textureOperator) { _textureSource->resetTextureOperator(textureOperator); - finishedLoading((bool)textureOperator); - emit networkTextureCreated(qWeakPointerCast (_self)); + if (!_isScheduledForDeletion) { + finishedLoading((bool)textureOperator); + auto thisTexture = std::dynamic_pointer_cast(shared_from_this()); + Q_ASSERT(thisTexture); + emit networkTextureCreated(thisTexture); + } } gpu::TexturePointer NetworkTexture::getFallbackTexture() const { @@ -507,7 +537,7 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const { class ImageReader : public QRunnable { public: - ImageReader(const QWeakPointer& resource, const QUrl& url, + ImageReader(const std::weak_ptr& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels, image::ColorChannel sourceChannel); void run() override final; @@ -516,7 +546,7 @@ class ImageReader : public QRunnable { private: static void listSupportedImageFormats(); - QWeakPointer _resource; + std::weak_ptr _resource; QUrl _url; QByteArray _content; size_t _extraHash; @@ -536,26 +566,34 @@ NetworkTexture::~NetworkTexture() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); } } const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max(); void NetworkTexture::makeRequest() { + // Prevent the texture from being scheduled from deletion while the request is being made + auto selfLocked = weak_from_this().lock(); + if (!selfLocked) { + qWarning() << "NetworkTexture::makeRequest: resource pointer has expired."; + Q_ASSERT(false); + return; + } + if (_currentlyLoadingResourceType != ResourceType::KTX) { Resource::makeRequest(); return; } if (isLocalUrl(_activeUrl)) { - auto self = _self; - QtConcurrent::run(QThreadPool::globalInstance(), [self] { + std::weak_ptr self = weak_from_this(); + QThreadPool::globalInstance()->start([self] { auto resource = self.lock(); if (!resource) { return; } - NetworkTexture* networkTexture = static_cast(resource.data()); + NetworkTexture* networkTexture = static_cast(resource.get()); networkTexture->makeLocalRequest(); }); return; @@ -611,7 +649,7 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleLocalRequestCompleted() { - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); } void NetworkTexture::makeLocalRequest() { @@ -668,7 +706,9 @@ void NetworkTexture::makeLocalRequest() { if (!textureAndSize.first) { qCDebug(networking).noquote() << "Failed load local KTX from" << path; - QMetaObject::invokeMethod(this, "setImage", + auto self = weak_from_this().lock(); + Q_ASSERT(self); + QMetaObject::invokeMethod(self.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -677,7 +717,9 @@ void NetworkTexture::makeLocalRequest() { _ktxResourceState = PENDING_MIP_REQUEST; _lowestKnownPopulatedMip = textureAndSize.first->minAvailableMipLevel(); - QMetaObject::invokeMethod(this, "setImage", + auto self = weak_from_this().lock(); + Q_ASSERT(self); + QMetaObject::invokeMethod(self.get(), "setImage", Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, textureAndSize.second.x), Q_ARG(int, textureAndSize.second.y)); @@ -699,7 +741,7 @@ bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) { } void NetworkTexture::startRequestForNextMipLevel() { - auto self = _self.lock(); + auto self = shared_from_this(); if (!self) { return; } @@ -782,7 +824,7 @@ void NetworkTexture::ktxInitialDataRequestFinished() { setSize(_bytesTotal); - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); auto result = _ktxHeaderRequest->getResult(); if (result == ResourceRequest::Success) { @@ -831,7 +873,7 @@ void NetworkTexture::ktxMipRequestFinished() { return; } - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); auto result = _ktxMipRequest->getResult(); if (result == ResourceRequest::Success) { @@ -842,13 +884,13 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxResourceState = WAITING_FOR_MIP_REQUEST; - auto self = _self; + auto self = weak_from_this(); auto url = _url; auto data = _ktxMipRequest->getData(); auto mipLevel = _ktxMipLevelRangeInFlight.first; auto texture = _textureSource->getGPUTexture(); DependencyManager::get()->incrementStat("PendingProcessing"); - QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] { + QThreadPool::globalInstance()->start([self, data, mipLevel, url, texture] { PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } }); DependencyManager::get()->decrementStat("PendingProcessing"); CounterStat counter("Processing"); @@ -876,12 +918,12 @@ void NetworkTexture::ktxMipRequestFinished() { return; } - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getHeight())); - QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + QMetaObject::invokeMethod(resource.get(), "startRequestForNextMipLevel"); }); } else { qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; @@ -913,10 +955,10 @@ void NetworkTexture::handleFinishedInitialLoad() { _ktxResourceState = WAITING_FOR_MIP_REQUEST; - auto self = _self; + auto self = weak_from_this(); auto url = _url; DependencyManager::get()->incrementStat("PendingProcessing"); - QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] { + QThreadPool::globalInstance()->start([self, ktxHeaderData, ktxHighMipData, url] { PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } }); DependencyManager::get()->decrementStat("PendingProcessing"); CounterStat counter("Processing"); @@ -937,7 +979,7 @@ void NetworkTexture::handleFinishedInitialLoad() { auto header = reinterpret_cast(ktxHeaderData.data()); if (!ktx::checkIdentifier(header->identifier)) { - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -946,7 +988,7 @@ void NetworkTexture::handleFinishedInitialLoad() { auto kvSize = header->bytesOfKeyValueData; if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -957,14 +999,14 @@ void NetworkTexture::handleFinishedInitialLoad() { auto imageDescriptors = header->generateImageDescriptors(); if (imageDescriptors.size() == 0) { - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); return; } auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors); - QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor", + QMetaObject::invokeMethod(resource.get(), "setOriginalDescriptor", Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor)); // Create bare ktx in memory @@ -975,7 +1017,7 @@ void NetworkTexture::handleFinishedInitialLoad() { std::string hash; if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { qWarning("Invalid source hash key found, bailing"); - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -1008,7 +1050,7 @@ void NetworkTexture::handleFinishedInitialLoad() { auto memKtx = ktx::KTX::createBare(*header, keyValues); if (!memKtx) { qWarning() << " Ktx could not be created, bailing"; - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -1022,7 +1064,7 @@ void NetworkTexture::handleFinishedInitialLoad() { auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) { qCWarning(materialnetworking) << url << " failed to write cache file"; - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -1057,12 +1099,12 @@ void NetworkTexture::handleFinishedInitialLoad() { textureAndSize = textureCache->cacheTextureByHash(filename, textureAndSize); } - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, textureAndSize.second.x), Q_ARG(int, textureAndSize.second.y)); - QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); + QMetaObject::invokeMethod(resource.get(), "startRequestForNextMipLevel"); }); } @@ -1072,7 +1114,7 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { } else if (_currentlyLoadingResourceType == ResourceType::ORIGINAL) { loadTextureContent(data); } else { - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); Resource::handleFailedRequest(ResourceRequest::Error); } } @@ -1103,8 +1145,11 @@ void NetworkTexture::loadMetaContent(const QByteArray& content) { _currentlyLoadingResourceType = ResourceType::KTX; _activeUrl = _activeUrl.resolved(url); auto textureCache = DependencyManager::get(); - auto self = _self.lock(); + auto self = weak_from_this().lock(); if (!self) { +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(!_wasDeleted); +#endif return; } QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); @@ -1119,7 +1164,7 @@ void NetworkTexture::loadMetaContent(const QByteArray& content) { _activeUrl = _activeUrl.resolved(meta.uncompressed); auto textureCache = DependencyManager::get(); - auto self = _self.lock(); + auto self = shared_from_this(); if (!self) { return; } @@ -1133,7 +1178,7 @@ void NetworkTexture::loadMetaContent(const QByteArray& content) { _activeUrl = _activeUrl.resolved(meta.original); auto textureCache = DependencyManager::get(); - auto self = _self.lock(); + auto self = shared_from_this(); if (!self) { return; } @@ -1151,7 +1196,7 @@ void NetworkTexture::loadTextureContent(const QByteArray& content) { return; } - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels, _sourceChannel)); + QThreadPool::globalInstance()->start(new ImageReader(weak_from_this(), _url, content, _extraHash, _maxNumPixels, _sourceChannel)); } void NetworkTexture::refresh() { @@ -1169,14 +1214,14 @@ void NetworkTexture::refresh() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - TextureCache::requestCompleted(_self); + TextureCache::requestCompleted(weak_from_this()); } _ktxResourceState = PENDING_INITIAL_LOAD; Resource::refresh(); } -ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels, image::ColorChannel sourceChannel) : +ImageReader::ImageReader(const std::weak_ptr& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels, image::ColorChannel sourceChannel) : _resource(resource), _url(url), _content(data), @@ -1235,7 +1280,8 @@ void ImageReader::read() { if (!resource) { return; } - auto networkTexture = resource.staticCast(); + auto networkTexture = std::dynamic_pointer_cast(resource); + Q_ASSERT(networkTexture); // Hash the source image and extraHash for KTX caching std::string hash; @@ -1268,7 +1314,7 @@ void ImageReader::read() { // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. if (textureAndSize.first) { - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, textureAndSize.second.x), Q_ARG(int, textureAndSize.second.y)); @@ -1290,7 +1336,7 @@ void ImageReader::read() { textureAndSize = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); if (!textureAndSize.first) { - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, 0), Q_ARG(int, 0)); @@ -1322,7 +1368,7 @@ void ImageReader::read() { textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } - QMetaObject::invokeMethod(resource.data(), "setImage", + QMetaObject::invokeMethod(resource.get(), "setImage", Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, textureAndSize.second.x), Q_ARG(int, textureAndSize.second.y)); @@ -1398,8 +1444,8 @@ NetworkTexturePointer TextureCache::getTextureByUUID(const QString& uuid) { if (!quuid.isNull()) { // We mark this as a resource texture because it's just a reference to another texture. The source // texture will be marked properly - NetworkTexturePointer toReturn = NetworkTexturePointer::create(uuid, true); - toReturn->setImageOperator(Texture::getTextureForUUIDOperator(uuid)); + NetworkTexturePointer toReturn = std::make_shared(uuid, true); + toReturn->setImageOperator(Texture::getTextureForUUIDOperator(QUuid(uuid))); return toReturn; } return NetworkTexturePointer(); diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h index 45172f9dd41..c0263edf8d2 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.h +++ b/libraries/material-networking/src/material-networking/TextureCache.h @@ -74,7 +74,7 @@ class NetworkTexture : public Resource, public Texture { void setExtra(void* extra) override; signals: - void networkTextureCreated(const QWeakPointer& self); + void networkTextureCreated(const std::weak_ptr& self); public slots: void ktxInitialDataRequestFinished(); @@ -152,9 +152,9 @@ public slots: friend class TextureCache; }; -using NetworkTexturePointer = QSharedPointer; +using NetworkTexturePointer = std::shared_ptr; -Q_DECLARE_METATYPE(QWeakPointer) +Q_DECLARE_METATYPE(std::weak_ptr) /// Stores cached textures, including render-to-texture targets. @@ -214,8 +214,8 @@ class TextureCache : public ResourceCache, public Dependency { // Overload ResourceCache::prefetch to allow specifying texture type for loads Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, image::ColorChannel sourceChannel = image::ColorChannel::NONE); - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; private: friend class ImageReader; diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 2027373db3c..ff517b1cbce 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -199,15 +199,14 @@ void Midi::MidiSetup() { MIDIINCAPS incaps; for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { if (MMSYSERR_NOERROR == midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS))) { - bool found = false; for (int j = 0; j < midiInExclude.size(); j++) { - if (midiInExclude[j].toStdString().compare(incaps.szPname) == 0) { + if (midiInExclude[j].toStdWString().compare(incaps.szPname) == 0) { found = true; break; } } - if (!found) { // EXCLUDE AN INPUT BY NAME + if (!found) { // EXCLUDE AN INPUT BY NAME HMIDIIN tmphin; if (MMSYSERR_NOERROR == midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION)) { if (MMSYSERR_NOERROR == midiInStart(tmphin)) { @@ -221,15 +220,14 @@ void Midi::MidiSetup() { MIDIOUTCAPS outcaps; for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { if (MMSYSERR_NOERROR == midiOutGetDevCaps(i, &outcaps, sizeof(MIDIOUTCAPS))) { - bool found = false; for (int j = 0; j < midiOutExclude.size(); j++) { - if (midiOutExclude[j].toStdString().compare(outcaps.szPname) == 0) { + if (midiOutExclude[j].toStdWString().compare(outcaps.szPname) == 0) { found = true; break; } } - if (!found) { // EXCLUDE AN OUTPUT BY NAME + if (!found) { // EXCLUDE AN OUTPUT BY NAME HMIDIOUT tmphout; if (MMSYSERR_NOERROR == midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION)) { midihout.push_back(tmphout); @@ -396,13 +394,13 @@ QStringList Midi::listMidiDevices(bool output) { MIDIOUTCAPS outcaps; for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); - rv.append(outcaps.szPname); + rv.append(QString::fromWCharArray(outcaps.szPname)); } } else { MIDIINCAPS incaps; for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); - rv.append(incaps.szPname); + rv.append(QString::fromWCharArray(incaps.szPname)); } } #endif diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index ee1ba6e8b36..2ec1ca50887 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -15,6 +15,7 @@ #define hifi_Midi_h #include +#include #include #include diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index 9fd3f520799..39d9ffd6e29 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -126,7 +126,7 @@ namespace baker { class BakerEngineBuilder { public: - using Input = VaryingSet3; + using Input = VaryingSet3; using Output = VaryingSet5, std::vector, std::vector>>; using JobModel = Task::ModelIO; void build(JobModel& model, const Varying& input, Varying& output) { @@ -193,7 +193,7 @@ namespace baker { } }; - Baker::Baker(const hfm::Model::Pointer& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& materialMappingBaseURL) : + Baker::Baker(const hfm::Model::Pointer& hfmModel, const hifi::VariantMultiHash& mapping, const hifi::URL& materialMappingBaseURL) : _engine(std::make_shared(BakerEngineBuilder::JobModel::create("Baker"), std::make_shared())) { _engine->feedInput(0, hfmModel); _engine->feedInput(1, mapping); diff --git a/libraries/model-baker/src/model-baker/Baker.h b/libraries/model-baker/src/model-baker/Baker.h index 9780484fa48..3bc9f7197b6 100644 --- a/libraries/model-baker/src/model-baker/Baker.h +++ b/libraries/model-baker/src/model-baker/Baker.h @@ -23,7 +23,7 @@ namespace baker { class Baker { public: - Baker(const hfm::Model::Pointer& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& materialMappingBaseURL); + Baker(const hfm::Model::Pointer& hfmModel, const hifi::VariantMultiHash& mapping, const hifi::URL& materialMappingBaseURL); std::shared_ptr getConfiguration(); diff --git a/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp b/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp index 48466ebe07a..1949b62906c 100644 --- a/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp +++ b/libraries/model-baker/src/model-baker/ParseFlowDataTask.cpp @@ -12,6 +12,7 @@ void ParseFlowDataTask::run(const baker::BakeContextPointer& context, const Inpu FlowData flowData; static const QString FLOW_PHYSICS_FIELD = "flowPhysicsData"; static const QString FLOW_COLLISIONS_FIELD = "flowCollisionsData"; + // QT6TODO: currently crashes on Qt6 for (auto mappingIter = mapping.cbegin(); mappingIter != mapping.cend(); mappingIter++) { if (mappingIter.key() == FLOW_PHYSICS_FIELD || mappingIter.key() == FLOW_COLLISIONS_FIELD) { QByteArray data = mappingIter.value().toByteArray(); diff --git a/libraries/model-baker/src/model-baker/ParseFlowDataTask.h b/libraries/model-baker/src/model-baker/ParseFlowDataTask.h index deabb63f79c..29e5224cd2d 100644 --- a/libraries/model-baker/src/model-baker/ParseFlowDataTask.h +++ b/libraries/model-baker/src/model-baker/ParseFlowDataTask.h @@ -18,7 +18,7 @@ class ParseFlowDataTask { public: - using Input = hifi::VariantHash; + using Input = hifi::VariantMultiHash; using Output = FlowData; using JobModel = baker::Job::ModelIO; diff --git a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp index c4c4f7f74b2..a6bbef97a99 100644 --- a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp +++ b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.cpp @@ -71,6 +71,9 @@ void ParseMaterialMappingTask::run(const baker::BakeContextPointer& context, con const auto& url = input.get1(); MaterialMapping materialMapping; + // QT6TODO: even listing keys causes crash + qDebug() << "ParseMaterialMappingTask::run " << mapping.keys(); + auto mappingIter = mapping.find("materialMap"); if (mappingIter != mapping.end()) { QByteArray materialMapValue = mappingIter.value().toByteArray(); diff --git a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h index 3e967a7d3f0..0e453c53c95 100644 --- a/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h +++ b/libraries/model-baker/src/model-baker/ParseMaterialMappingTask.h @@ -22,11 +22,11 @@ class ParseMaterialMappingTask { public: - using Input = baker::VaryingSet2; + using Input = baker::VaryingSet2; using Output = MaterialMapping; using JobModel = baker::Job::ModelIO; void run(const baker::BakeContextPointer& context, const Input& input, Output& output); }; -#endif // hifi_ParseMaterialMappingTask_h \ No newline at end of file +#endif // hifi_ParseMaterialMappingTask_h diff --git a/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp b/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp index 662d5f36dc6..9685d4369e2 100644 --- a/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp +++ b/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp @@ -13,10 +13,10 @@ #include "ModelBakerLogging.h" -QMap getJointNameMapping(const hifi::VariantHash& mapping) { +QMap getJointNameMapping(const hifi::VariantMultiHash& mapping) { static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; QMap hfmToHifiJointNameMap; - if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) { + if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].typeId() == QMetaType::QVariantHash) { auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash(); for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) { hfmToHifiJointNameMap.insert(itr.key(), itr.value().toString()); @@ -26,11 +26,13 @@ QMap getJointNameMapping(const hifi::VariantHash& mapping) { return hfmToHifiJointNameMap; } -QMap getJointRotationOffsets(const hifi::VariantHash& mapping) { +QMap getJointRotationOffsets(const hifi::VariantMultiHash& mapping) { QMap jointRotationOffsets; static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; static const QString JOINT_ROTATION_OFFSET2_FIELD = "jointRotationOffset2"; - if (!mapping.isEmpty() && ((mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) || (mapping.contains(JOINT_ROTATION_OFFSET2_FIELD) && mapping[JOINT_ROTATION_OFFSET2_FIELD].type() == QVariant::Hash))) { + if (!mapping.isEmpty() && ((mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].typeId() == QMetaType::QVariantHash) + || (mapping.contains(JOINT_ROTATION_OFFSET2_FIELD) && mapping[JOINT_ROTATION_OFFSET2_FIELD].typeId() == QMetaType::QVariantHash))) { + QHash offsets; if (mapping.contains(JOINT_ROTATION_OFFSET_FIELD)) { offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); @@ -72,7 +74,7 @@ void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Inpu auto& jointRotationOffsets = output.edit1(); static const QString JOINT_ROTATION_OFFSET2_FIELD = "jointRotationOffset2"; - QVariantHash fstHashMap = mapping; + hifi::VariantMultiHash fstHashMap = mapping; bool newJointRot = fstHashMap.contains(JOINT_ROTATION_OFFSET2_FIELD); // Get joint renames diff --git a/libraries/model-baker/src/model-baker/PrepareJointsTask.h b/libraries/model-baker/src/model-baker/PrepareJointsTask.h index 802dbb38260..76960b8f92d 100644 --- a/libraries/model-baker/src/model-baker/PrepareJointsTask.h +++ b/libraries/model-baker/src/model-baker/PrepareJointsTask.h @@ -29,7 +29,7 @@ class PrepareJointsConfig : public baker::JobConfig { class PrepareJointsTask { public: using Config = PrepareJointsConfig; - using Input = baker::VaryingSet2, hifi::VariantHash /*mapping*/>; + using Input = baker::VaryingSet2, hifi::VariantMultiHash /*mapping*/>; using Output = baker::VaryingSet3, QMap /*jointRotationOffsets*/, QHash /*jointIndices*/>; using JobModel = baker::Job::ModelIO; @@ -40,4 +40,4 @@ class PrepareJointsTask { bool _passthrough { false }; }; -#endif // hifi_PrepareJointsTask_h \ No newline at end of file +#endif // hifi_PrepareJointsTask_h diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 264c6b98019..f4ac0c4e7f7 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -29,6 +29,8 @@ #include #include +#include "shared/QtHelpers.h" + Q_LOGGING_CATEGORY(trace_resource_parse_geometry, "trace.resource.parse.geometry") class GeometryExtra { @@ -67,6 +69,15 @@ namespace std { } }; + template <> + struct hash { + size_t operator()(const hifi::VariantMultiHash& a) const { + QVariantHasher hasher; + // QT6TODO: check with profiler if this is not causing performance problems + return hasher.hash(qMultiHashToQVariant(a)); + } + }; + template <> struct hash { size_t operator()(const QUrl& a) const { @@ -87,7 +98,7 @@ namespace std { class GeometryReader : public QRunnable { public: - GeometryReader(const ModelLoader& modelLoader, QWeakPointer& resource, const QUrl& url, const GeometryMappingPair& mapping, + GeometryReader(const ModelLoader& modelLoader, std::weak_ptr resource, const QUrl& url, const GeometryMappingPair& mapping, const QByteArray& data, bool combineParts, const QString& webMediaType) : _modelLoader(modelLoader), _resource(resource), _url(url), _mapping(mapping), _data(data), _combineParts(combineParts), _webMediaType(webMediaType) { @@ -98,8 +109,9 @@ class GeometryReader : public QRunnable { private: ModelLoader _modelLoader; - QWeakPointer _resource; + std::weak_ptr _resource; QUrl _url; + // QT6TODO: I'm not sure if _mapping should be QHash or QMultiHash GeometryMappingPair _mapping; QByteArray _data; bool _combineParts; @@ -119,7 +131,8 @@ void GeometryReader::run() { QThread::currentThread()->setPriority(originalPriority); }); - if (!_resource.toStrongRef().data()) { + if (!_resource.lock()) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; return; } @@ -129,7 +142,7 @@ void GeometryReader::run() { } // Ensure the resource has not been deleted - auto resource = _resource.toStrongRef(); + auto resource = _resource.lock(); if (!resource) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; return; @@ -140,9 +153,9 @@ void GeometryReader::run() { } HFMModel::Pointer hfmModel; - QMultiHash serializerMapping = _mapping.second; - serializerMapping.replace("combineParts",_combineParts); - serializerMapping.replace("deduplicateIndices", true); + hifi::VariantMultiHash serializerMapping = _mapping.second; + serializerMapping["combineParts"] = _combineParts; + serializerMapping["deduplicateIndices"] = true; if (_url.path().toLower().endsWith(".gz")) { QByteArray uncompressedData; @@ -181,19 +194,19 @@ void GeometryReader::run() { auto processedHFMModel = modelBaker.getHFMModel(); auto materialMapping = modelBaker.getMaterialMapping(); - QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", + QMetaObject::invokeMethod(resource.get(), "setGeometryDefinition", Q_ARG(HFMModel::Pointer, processedHFMModel), Q_ARG(MaterialMapping, materialMapping)); } catch (const std::exception&) { - auto resource = _resource.toStrongRef(); + auto resource = _resource.lock(); if (resource) { - QMetaObject::invokeMethod(resource.data(), "finishedLoading", + QMetaObject::invokeMethod(resource.get(), "finishedLoading", Q_ARG(bool, false)); } } catch (QString& e) { qCWarning(modelnetworking) << "Exception while loading model --" << e; - auto resource = _resource.toStrongRef(); + auto resource = _resource.lock(); if (resource) { - QMetaObject::invokeMethod(resource.data(), "finishedLoading", + QMetaObject::invokeMethod(resource.get(), "finishedLoading", Q_ARG(bool, false)); } } @@ -269,7 +282,8 @@ void GeometryResource::downloadFinished(const QByteArray& data) { GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; // Get the raw GeometryResource - _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); + _geometryResource = std::dynamic_pointer_cast(modelCache->getResource(url, QUrl(), &extra, std::hash()(extra))); + Q_ASSERT(_geometryResource); // Avoid caching nested resources - their references will be held by the parent _geometryResource->_isCacheable = false; @@ -280,7 +294,7 @@ void GeometryResource::downloadFinished(const QByteArray& data) { disconnect(_connection); } - _connection = connect(_geometryResource.data(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded); + _connection = connect(_geometryResource.get(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded); } } } else { @@ -288,7 +302,7 @@ void GeometryResource::downloadFinished(const QByteArray& data) { _url = _effectiveBaseURL; _textureBaseURL = _effectiveBaseURL; } - QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mappingPair, data, _combineParts, _request->getWebMediaType())); + QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, weak_from_this(), _effectiveBaseURL, _mappingPair, data, _combineParts, _request->getWebMediaType())); } } @@ -389,18 +403,20 @@ ModelCache::ModelCache() { modelFormatRegistry->addFormat(GLTFSerializer()); } -QSharedPointer ModelCache::createResource(const QUrl& url) { - return QSharedPointer(new GeometryResource(url, _modelLoader), &GeometryResource::deleter); +std::shared_ptr ModelCache::createResource(const QUrl& url) { + return std::shared_ptr(new GeometryResource(url, _modelLoader), GeometryResource::sharedPtrDeleter); } -QSharedPointer ModelCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new GeometryResource(*resource.staticCast()), &GeometryResource::deleter); +std::shared_ptr ModelCache::createResourceCopy(const std::shared_ptr& resource) { + auto geometryResource = std::dynamic_pointer_cast(resource); + Q_ASSERT(geometryResource); + return std::shared_ptr(new GeometryResource(*geometryResource), GeometryResource::sharedPtrDeleter); } GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { bool combineParts = true; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; - GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); + GeometryResource::Pointer resource = std::dynamic_pointer_cast(getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra))); if (resource) { if (resource->isLoaded() && resource->shouldSetTextures()) { resource->setTextures(); @@ -414,7 +430,7 @@ GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& u const QUrl& textureBaseUrl) { bool combineParts = false; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; - GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); + GeometryResource::Pointer resource = std::dynamic_pointer_cast(getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra))); if (resource) { if (resource->isLoaded() && resource->shouldSetTextures()) { resource->setTextures(); @@ -533,16 +549,16 @@ const std::shared_ptr Geometry::getShapeMaterial(int partID) co } void GeometryResourceWatcher::startWatching() { - connect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); - connect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); + connect(_resource.get(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); + connect(_resource.get(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); if (_resource->isLoaded()) { resourceFinished(!_resource->getURL().isEmpty()); } } void GeometryResourceWatcher::stopWatching() { - disconnect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); - disconnect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); + disconnect(_resource.get(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); + disconnect(_resource.get(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); } void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 79021087095..9969ceba449 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -26,7 +26,7 @@ class MeshPart; -using GeometryMappingPair = std::pair; +using GeometryMappingPair = std::pair; Q_DECLARE_METATYPE(GeometryMappingPair) class Geometry { @@ -59,7 +59,7 @@ class Geometry { virtual bool areTexturesLoaded() const; const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; } bool shouldWaitForWearables() const { return _waitForWearables; } - const QVariantHash& getMapping() const { return _mapping; } + const hifi::VariantMultiHash& getMapping() const { return _mapping; } protected: // Shared across all geometries, constant throughout lifetime @@ -72,21 +72,22 @@ class Geometry { NetworkMaterials _materials; QUrl _animGraphOverrideUrl; - QVariantHash _mapping; // parsed contents of FST file. + hifi::VariantMultiHash _mapping; // parsed contents of FST file. bool _waitForWearables { false }; private: - mutable bool _areTexturesLoaded { false }; + mutable std::atomic _areTexturesLoaded { false }; }; /// A geometry loaded from the network. class GeometryResource : public Resource, public Geometry { Q_OBJECT public: - using Pointer = QSharedPointer; + using Pointer = std::shared_ptr; GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {} GeometryResource(const GeometryResource& other); + virtual ~GeometryResource() {} QString getType() const override { return "Geometry"; } @@ -175,8 +176,8 @@ class ModelCache : public ResourceCache, public Dependency { protected: friend class GeometryResource; - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; private: ModelCache(); diff --git a/libraries/model-networking/src/model-networking/ModelLoader.cpp b/libraries/model-networking/src/model-networking/ModelLoader.cpp index 65314633c96..63282ac7a10 100644 --- a/libraries/model-networking/src/model-networking/ModelLoader.cpp +++ b/libraries/model-networking/src/model-networking/ModelLoader.cpp @@ -15,7 +15,7 @@ #include -hfm::Model::Pointer ModelLoader::load(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url, const std::string& webMediaType) const { +hfm::Model::Pointer ModelLoader::load(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url, const std::string& webMediaType) const { auto serializer = DependencyManager::get()->getSerializerForMediaType(data, url, webMediaType); if (!serializer) { return hfm::Model::Pointer(); diff --git a/libraries/model-networking/src/model-networking/ModelLoader.h b/libraries/model-networking/src/model-networking/ModelLoader.h index 5fbab4fb650..095fa5c22d8 100644 --- a/libraries/model-networking/src/model-networking/ModelLoader.h +++ b/libraries/model-networking/src/model-networking/ModelLoader.h @@ -20,7 +20,7 @@ class ModelLoader { // Given the currently stored list of supported file formats, determine how to load a model from the given parameters. // If successful, return an owned reference to the newly loaded model. // If failed, return an empty reference. - hfm::Model::Pointer load(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url, const std::string& webMediaType) const; + hfm::Model::Pointer load(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url, const std::string& webMediaType) const; }; #endif // hifi_ModelLoader_h diff --git a/libraries/model-serializers/src/FBXSerializer.cpp b/libraries/model-serializers/src/FBXSerializer.cpp index 58eed40bdb4..8115b8c541b 100644 --- a/libraries/model-serializers/src/FBXSerializer.cpp +++ b/libraries/model-serializers/src/FBXSerializer.cpp @@ -23,6 +23,8 @@ #include +#include "shared/QtHelpers.h" + // TOOL: Uncomment the following line to enable the filtering of all the unknown fields of a node so we can break point easily while loading a model with problems... //#define DEBUG_FBXSERIALIZER @@ -34,7 +36,7 @@ glm::vec3 parseVec3(const QString& string) { return glm::vec3(); } glm::vec3 value; - for (int i = 0; i < 3; i++) { + for (qsizetype i = 0; i < 3; i++) { // duplicate last value if there aren't three elements value[i] = elements.at(min(i, elements.size() - 1)).trimmed().toFloat(); } @@ -103,10 +105,10 @@ QString getModelName(const QVariantList& properties) { QString name; if (properties.size() == 3) { name = properties.at(1).toString(); - name = processID(name.left(name.indexOf(QChar('\0')))); } else { - name = processID(properties.at(0).toString()); + name = properties.at(0).toString(); } + name = processID(name.left(name.indexOf(QChar('\0')))); return name; } @@ -114,10 +116,10 @@ QString getMaterialName(const QVariantList& properties) { QString name; if (properties.size() == 1 || properties.at(1).toString().isEmpty()) { name = properties.at(0).toString(); - name = processID(name.left(name.indexOf(QChar('\0')))); } else { - name = processID(properties.at(1).toString()); + name = properties.at(1).toString(); } + name = processID(name.left(name.indexOf(QChar('\0')))); return name; } @@ -260,7 +262,7 @@ typedef QPair WeightedIndex; void addBlendshapes(const ExtractedBlendshape& extracted, const QList& indices, ExtractedMesh& extractedMesh) { foreach (const WeightedIndex& index, indices) { - extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), index.first + 1)); + extractedMesh.mesh.blendshapes.resize(max(extractedMesh.mesh.blendshapes.size(), static_cast(index.first + 1))); extractedMesh.blendshapeIndexMaps.resize(extractedMesh.mesh.blendshapes.size()); HFMBlendshape& blendshape = extractedMesh.mesh.blendshapes[index.first]; QHash& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; @@ -394,7 +396,7 @@ hifi::ByteArray fileOnUrl(const hifi::ByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const QString& url) { +HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantMultiHash& mapping, const QString& url) { const FBXNode& node = _rootNode; bool deduplicateIndices = mapping["deduplicateIndices"].toBool(); @@ -419,7 +421,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const std::map lights; - hifi::VariantMultiHash blendshapeMappings = mapping.value("bs").toHash(); + hifi::VariantMultiHash blendshapeMappings = qVariantToQMultiHash(mapping.value("bs")); QMultiHash blendshapeIndices; for (int i = 0;; i++) { @@ -490,7 +492,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const foreach (const FBXNode& subsubobject, subobject.children) { static const QVariant APPLICATION_NAME = QVariant(hifi::ByteArray("Original|ApplicationName")); if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && - subsubobject.properties.at(0) == APPLICATION_NAME) { + subsubobject.properties.at(0).toByteArray() == APPLICATION_NAME) { hfmModel.applicationName = subsubobject.properties.at(4).toString(); } } @@ -510,7 +512,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const static const QVariant UNIT_SCALE_FACTOR = hifi::ByteArray("UnitScaleFactor"); static const QVariant AMBIENT_COLOR = hifi::ByteArray("AmbientColor"); static const QVariant UP_AXIS = hifi::ByteArray("UpAxis"); - const auto& subpropName = subobject.properties.at(0); + const auto& subpropName = subobject.properties.at(0).toByteArray(); if (subpropName == UNIT_SCALE_FACTOR) { unitScaleFactor = subobject.properties.at(index).toFloat(); } else if (subpropName == AMBIENT_COLOR) { @@ -533,7 +535,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } else if (child.name == "Objects") { foreach (const FBXNode& object, child.children) { if (object.name == "Geometry") { - if (object.properties.at(2) == "Mesh") { + if (object.properties.at(2).toByteArray() == "Mesh") { meshes.insert(getID(object.properties), extractMesh(object, meshIndex, deduplicateIndices)); } else { // object.properties.at(2) == "Shape" ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) }; @@ -567,7 +569,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const glm::vec3 rotationMin, rotationMax; - bool isLimbNode = object.properties.size() >= 3 && object.properties.at(2) == "LimbNode"; + bool isLimbNode = object.properties.size() >= 3 && object.properties.at(2).toByteArray() == "LimbNode"; FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::vec3(), glm::vec3(), false, glm::vec3(), glm::quat(), glm::vec3(1.0f), isLimbNode }; @@ -610,7 +612,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const static const QVariant PRE_ROTATION = hifi::ByteArray("PreRotation"); static const QVariant POST_ROTATION = hifi::ByteArray("PostRotation"); foreach(const FBXNode& property, subobject.children) { - const auto& childProperty = property.properties.at(0); + const auto& childProperty = property.properties.at(0).toByteArray(); if (property.name == propertyName) { if (childProperty == LCL_TRANSLATION) { translation = getVec3(property.properties, index); @@ -791,18 +793,18 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const static const QVariant SCALING = hifi::ByteArray("Scaling"); if (property.name == propertyName) { QString v = property.properties.at(0).toString(); - if (property.properties.at(0) == UV_SET) { + if (property.properties.at(0).toByteArray() == UV_SET) { std::string uvName = property.properties.at(index).toString().toStdString(); tex.assign(tex.UVSet, property.properties.at(index).toString()); - } else if (property.properties.at(0) == CURRENT_TEXTURE_BLEND_MODE) { + } else if (property.properties.at(0).toByteArray() == CURRENT_TEXTURE_BLEND_MODE) { tex.assign(tex.currentTextureBlendMode, property.properties.at(index).value()); - } else if (property.properties.at(0) == USE_MATERIAL) { + } else if (property.properties.at(0).toByteArray() == USE_MATERIAL) { tex.assign(tex.useMaterial, property.properties.at(index).value()); - } else if (property.properties.at(0) == TRANSLATION) { + } else if (property.properties.at(0).toByteArray() == TRANSLATION) { tex.assign(tex.translation, tex.translation + getVec3(property.properties, index)); - } else if (property.properties.at(0) == ROTATION) { + } else if (property.properties.at(0).toByteArray() == ROTATION) { tex.assign(tex.rotation, getVec3(property.properties, index)); - } else if (property.properties.at(0) == SCALING) { + } else if (property.properties.at(0).toByteArray() == SCALING) { auto newScaling = getVec3(property.properties, index); if (newScaling.x == 0.0f) { newScaling.x = 1.0f; @@ -917,41 +919,41 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const foreach(const FBXNode& property, subobject.children) { if (property.name == propertyName) { - if (property.properties.at(0) == DIFFUSE_COLOR) { + if (property.properties.at(0).toByteArray() == DIFFUSE_COLOR) { material.diffuseColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == DIFFUSE_FACTOR) { + } else if (property.properties.at(0).toByteArray() == DIFFUSE_FACTOR) { material.diffuseFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == DIFFUSE) { + } else if (property.properties.at(0).toByteArray() == DIFFUSE) { // NOTE: this is uneeded but keep it for now for debug // material.diffuseColor = getVec3(property.properties, index); // material.diffuseFactor = 1.0; - } else if (property.properties.at(0) == SPECULAR_COLOR) { + } else if (property.properties.at(0).toByteArray() == SPECULAR_COLOR) { material.specularColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == SPECULAR_FACTOR) { + } else if (property.properties.at(0).toByteArray() == SPECULAR_FACTOR) { material.specularFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == SPECULAR) { + } else if (property.properties.at(0).toByteArray() == SPECULAR) { // NOTE: this is uneeded but keep it for now for debug // material.specularColor = getVec3(property.properties, index); // material.specularFactor = 1.0; - } else if (property.properties.at(0) == EMISSIVE_COLOR) { + } else if (property.properties.at(0).toByteArray() == EMISSIVE_COLOR) { material.emissiveColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == EMISSIVE_FACTOR) { + } else if (property.properties.at(0).toByteArray() == EMISSIVE_FACTOR) { material.emissiveFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == EMISSIVE) { + } else if (property.properties.at(0).toByteArray() == EMISSIVE) { // NOTE: this is uneeded but keep it for now for debug // material.emissiveColor = getVec3(property.properties, index); // material.emissiveFactor = 1.0; - } else if (property.properties.at(0) == AMBIENT_FACTOR) { + } else if (property.properties.at(0).toByteArray() == AMBIENT_FACTOR) { material.ambientFactor = property.properties.at(index).value(); // Detected just for Blender AO vs lightmap - } else if (property.properties.at(0) == SHININESS) { + } else if (property.properties.at(0).toByteArray() == SHININESS) { material.shininess = property.properties.at(index).value(); - } else if (property.properties.at(0) == OPACITY) { + } else if (property.properties.at(0).toByteArray() == OPACITY) { material.opacity = property.properties.at(index).value(); - } else if (property.properties.at(0) == REFLECTION_FACTOR) { + } else if (property.properties.at(0).toByteArray() == REFLECTION_FACTOR) { // Blender 2.79 and below set REFLECTION_FACTOR, but there is no way to actually change that value in their UI, // so we are falling back to non-PBS material. if (isBlenderVersionLower280 == true) { @@ -963,51 +965,51 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } // Sting Ray Material Properties!!!! - else if (property.properties.at(0) == MAYA_USE_NORMAL_MAP) { + else if (property.properties.at(0).toByteArray() == MAYA_USE_NORMAL_MAP) { material.isPBSMaterial = true; material.useNormalMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_BASE_COLOR) { + } else if (property.properties.at(0).toByteArray() == MAYA_BASE_COLOR) { material.isPBSMaterial = true; material.diffuseColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == MAYA_USE_COLOR_MAP) { + } else if (property.properties.at(0).toByteArray() == MAYA_USE_COLOR_MAP) { material.isPBSMaterial = true; material.useAlbedoMap = (bool) property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_ROUGHNESS) { + } else if (property.properties.at(0).toByteArray() == MAYA_ROUGHNESS) { material.isPBSMaterial = true; material.roughness = property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_USE_ROUGHNESS_MAP) { + } else if (property.properties.at(0).toByteArray() == MAYA_USE_ROUGHNESS_MAP) { material.isPBSMaterial = true; material.useRoughnessMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_METALLIC) { + } else if (property.properties.at(0).toByteArray() == MAYA_METALLIC) { material.isPBSMaterial = true; material.metallic = property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_USE_METALLIC_MAP) { + } else if (property.properties.at(0).toByteArray() == MAYA_USE_METALLIC_MAP) { material.isPBSMaterial = true; material.useMetallicMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_EMISSIVE) { + } else if (property.properties.at(0).toByteArray() == MAYA_EMISSIVE) { material.isPBSMaterial = true; material.emissiveColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == MAYA_EMISSIVE_INTENSITY) { + } else if (property.properties.at(0).toByteArray() == MAYA_EMISSIVE_INTENSITY) { material.isPBSMaterial = true; material.emissiveIntensity = property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_USE_EMISSIVE_MAP) { + } else if (property.properties.at(0).toByteArray() == MAYA_USE_EMISSIVE_MAP) { material.isPBSMaterial = true; material.useEmissiveMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_USE_AO_MAP) { + } else if (property.properties.at(0).toByteArray() == MAYA_USE_AO_MAP) { material.isPBSMaterial = true; material.useOcclusionMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == MAYA_UV_SCALE) { + } else if (property.properties.at(0).toByteArray() == MAYA_UV_SCALE) { if (property.properties.size() == MAYA_UV_SCALE_PROPERTY_LENGTH) { // properties: { "Maya|uv_scale", "Vector2D", "Vector2", nothing, double, double } glm::vec3 scale = glm::vec3(property.properties.at(4).value(), property.properties.at(5).value(), 1.0); @@ -1022,7 +1024,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } materialParam.scaling *= scale; } - } else if (property.properties.at(0) == MAYA_UV_OFFSET) { + } else if (property.properties.at(0).toByteArray() == MAYA_UV_OFFSET) { if (property.properties.size() == MAYA_UV_OFFSET_PROPERTY_LENGTH) { // properties: { "Maya|uv_offset", "Vector2D", "Vector2", nothing, double, double } glm::vec3 translation = glm::vec3(property.properties.at(4).value(), property.properties.at(5).value(), 1.0); @@ -1076,7 +1078,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } } else if (object.name == "Deformer") { - if (object.properties.last() == "Cluster") { + if (object.properties.last().toByteArray() == "Cluster") { Cluster cluster; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "Indexes") { @@ -1096,7 +1098,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const clusters.insert(getID(object.properties), cluster); } - } else if (object.properties.last() == "BlendShapeChannel") { + } else if (object.properties.last().toByteArray() == "BlendShapeChannel") { hifi::ByteArray name = object.properties.at(1).toByteArray(); name = name.left(name.indexOf('\0')); @@ -1138,7 +1140,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const static const QVariant OP = hifi::ByteArray("OP"); foreach (const FBXNode& connection, child.children) { if (connection.name == "C" || connection.name == "Connect") { - if (connection.properties.at(0) == OO) { + if (connection.properties.at(0).toByteArray() == OO) { QString childID = getID(connection.properties, 1); QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); @@ -1152,7 +1154,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const _lightmapOffset = glm::clamp((*lightIt).second.color.x, 0.f, 1.f); } } - } else if (connection.properties.at(0) == OP) { + } else if (connection.properties.at(0).toByteArray() == OP) { int counter = 0; hifi::ByteArray type = connection.properties.at(3).toByteArray().toLower(); if (type.contains("DiffuseFactor")) { @@ -1394,7 +1396,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } // NOTE: shapeVertices are in joint-frame - hfmModel.shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); + hfmModel.shapeVertices.resize(std::max(qsizetype(1), hfmModel.joints.size()) ); hfmModel.bindExtents.reset(); hfmModel.meshExtents.reset(); @@ -1700,7 +1702,7 @@ std::unique_ptr FBXSerializer::getFactory() const { return std::make_unique>(); } -HFMModel::Pointer FBXSerializer::read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url) { +HFMModel::Pointer FBXSerializer::read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url) { QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); diff --git a/libraries/model-serializers/src/FBXSerializer.h b/libraries/model-serializers/src/FBXSerializer.h index e528885754c..f6fe0e42313 100644 --- a/libraries/model-serializers/src/FBXSerializer.h +++ b/libraries/model-serializers/src/FBXSerializer.h @@ -112,12 +112,12 @@ class FBXSerializer : public HFMSerializer { HFMModel* _hfmModel; /// Reads HFMModel from the supplied model and mapping data. /// \exception QString if an error occurs in parsing - HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; + HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url = hifi::URL()) override; FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); - HFMModel* extractHFMModel(const hifi::VariantHash& mapping, const QString& url); + HFMModel* extractHFMModel(const hifi::VariantMultiHash& mapping, const QString& url); static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate); QHash meshes; diff --git a/libraries/model-serializers/src/FBXSerializer_Mesh.cpp b/libraries/model-serializers/src/FBXSerializer_Mesh.cpp index 90942c877ae..f2c1894dd1e 100644 --- a/libraries/model-serializers/src/FBXSerializer_Mesh.cpp +++ b/libraries/model-serializers/src/FBXSerializer_Mesh.cpp @@ -220,10 +220,10 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me } else if (subdata.name == "NormalsIndex") { data.normalIndices = getIntVector(subdata); - } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { + } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0).toByteArray() == BY_VERTICE) { data.normalsByVertex = true; - } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) { + } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0).toByteArray() == INDEX_TO_DIRECT) { indexToDirect = true; } } @@ -240,10 +240,10 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me } else if (subdata.name == "ColorsIndex" || subdata.name == "ColorIndex") { data.colorIndices = getIntVector(subdata); - } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { + } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0).toByteArray() == BY_VERTICE) { data.colorsByVertex = true; - } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) { + } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0).toByteArray() == INDEX_TO_DIRECT) { indexToDirect = true; } } @@ -334,7 +334,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me if (subdata.name == "Materials") { materials = getIntVector(subdata); } else if (subdata.name == "MappingInformationType") { - if (subdata.properties.at(0) == BY_POLYGON) { + if (subdata.properties.at(0).toByteArray() == BY_POLYGON) { isMaterialPerPolygon = true; } } else { diff --git a/libraries/model-serializers/src/FBXToJSON.h b/libraries/model-serializers/src/FBXToJSON.h index f36235abca8..6d9117d0326 100644 --- a/libraries/model-serializers/src/FBXToJSON.h +++ b/libraries/model-serializers/src/FBXToJSON.h @@ -15,9 +15,11 @@ #include #include +#include + // Forward declarations. class FBXNode; -template class QVector; +//template class QVector; class FBXToJSON : public std::ostringstream { public: diff --git a/libraries/model-serializers/src/FBXWriter.cpp b/libraries/model-serializers/src/FBXWriter.cpp index efc94461aa0..05708ec9391 100644 --- a/libraries/model-serializers/src/FBXWriter.cpp +++ b/libraries/model-serializers/src/FBXWriter.cpp @@ -12,6 +12,7 @@ #include "FBXWriter.h" #include +#include #ifdef USE_FBX_2016_FORMAT using FBXEndOffset = int64_t; @@ -131,7 +132,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << prop.value(); break; - case QVariant::Type::Bool: + case QMetaType::Bool: out.device()->write("C", 1); out << prop.toBool(); break; diff --git a/libraries/model-serializers/src/FST.cpp b/libraries/model-serializers/src/FST.cpp index 8d03933717b..080df2ce500 100644 --- a/libraries/model-serializers/src/FST.cpp +++ b/libraries/model-serializers/src/FST.cpp @@ -15,6 +15,8 @@ #include #include +#include "shared/QtHelpers.h" + constexpr float DEFAULT_SCALE { 1.0f }; FST::FST(QString fstPath, QMultiHash data) : _fstPath(std::move(fstPath)) { @@ -40,7 +42,7 @@ FST::FST(QString fstPath, QMultiHash data) : _fstPath(std::mo } FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePath, const hfm::Model& hfmModel) { - QVariantHash mapping; + hifi::VariantMultiHash mapping; // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file @@ -136,7 +138,7 @@ FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePat blendshapes.insert("Sneer", QVariantList() << "NoseScrunch_Right" << 0.75); blendshapes.insert("Sneer", QVariantList() << "Squint_Left" << 0.5); blendshapes.insert("Sneer", QVariantList() << "Squint_Right" << 0.5); - mapping.insert(BLENDSHAPE_FIELD, blendshapes); + mapping.insert(BLENDSHAPE_FIELD, qMultiHashToQVariant(blendshapes)); } return new FST(fstPath, mapping); } diff --git a/libraries/model-serializers/src/FST.h b/libraries/model-serializers/src/FST.h index e1273987a26..aed46a49cfe 100644 --- a/libraries/model-serializers/src/FST.h +++ b/libraries/model-serializers/src/FST.h @@ -57,7 +57,7 @@ class FST : public QObject { QStringList _scriptPaths{}; - QVariantHash _other{}; + hifi::VariantMultiHash _other{}; }; #endif // hifi_FST_h diff --git a/libraries/model-serializers/src/FSTReader.cpp b/libraries/model-serializers/src/FSTReader.cpp index 9c1ff5a431d..058c25b63a0 100644 --- a/libraries/model-serializers/src/FSTReader.cpp +++ b/libraries/model-serializers/src/FSTReader.cpp @@ -21,6 +21,8 @@ #include #include +#include "shared/QtHelpers.h" + const QStringList SINGLE_VALUE_PROPERTIES { NAME_FIELD, FILENAME_FIELD, TEXDIR_FIELD, SCRIPT_FIELD, WAIT_FOR_WEARABLES_FIELD, COMMENT_FIELD }; @@ -73,7 +75,7 @@ hifi::VariantMultiHash FSTReader::parseMapping(QIODevice* device) { return properties; } -static void removeBlendshape(QVariantHash& bs, const QString& key) { +static void removeBlendshape(hifi::VariantMultiHash& bs, const QString& key) { if (bs.contains(key)) { bs.remove(key); } @@ -96,8 +98,9 @@ static void splitBlendshapes(hifi::VariantMultiHash& bs, const QString& key, con } // convert legacy blendshapes to arkit blendshapes +// QT6TODO: I'm not sure if qVariantToQMultiHash will work correctly. static void fixUpLegacyBlendshapes(hifi::VariantMultiHash & properties) { - hifi::VariantMultiHash bs = properties.value("bs").toHash(); + hifi::VariantMultiHash bs = qVariantToQMultiHash(properties.value("bs")); // These blendshapes have no ARKit equivalent, so we remove them. removeBlendshape(bs, "JawChew"); @@ -112,7 +115,8 @@ static void fixUpLegacyBlendshapes(hifi::VariantMultiHash & properties) { splitBlendshapes(bs, "Sneer", "NoseSneer_L", "NoseSneer_R"); // re-insert new mutated bs hash into mapping properties. - properties.insert("bs", bs); + // QT6TODO: add VariantMultiHash to QVariant conversion + properties.insert("bs", qMultiHashToQVariant(bs)); } hifi::VariantMultiHash FSTReader::readMapping(const QByteArray& data) { @@ -123,7 +127,7 @@ hifi::VariantMultiHash FSTReader::readMapping(const QByteArray& data) { return mapping; } -void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { +void FSTReader::writeVariant(QBuffer& buffer, const hifi::VariantMultiHash::const_iterator& it) { QByteArray key = it.key().toUtf8() + " = "; QVariantHash hashValue = it.value().toHash(); if (hashValue.isEmpty()) { @@ -202,7 +206,7 @@ FSTReader::ModelType FSTReader::predictModelType(const hifi::VariantMultiHash& m QVariantHash joints; - if (mapping.contains("joint") && mapping.value("joint").type() == QVariant::Hash) { + if (mapping.contains("joint") && mapping.value("joint").typeId() == QMetaType::QVariantHash) { joints = mapping.value("joint").toHash(); } diff --git a/libraries/model-serializers/src/FSTReader.h b/libraries/model-serializers/src/FSTReader.h index 5557df67c69..4becf1c9275 100644 --- a/libraries/model-serializers/src/FSTReader.h +++ b/libraries/model-serializers/src/FSTReader.h @@ -55,14 +55,14 @@ class FSTReader { /// Predicts the type of model by examining the mapping static ModelType predictModelType(const hifi::VariantMultiHash& mapping); - static QVector getScripts(const QUrl& fstUrl, const hifi::VariantMultiHash& mapping = QVariantHash()); + static QVector getScripts(const QUrl& fstUrl, const hifi::VariantMultiHash& mapping = hifi::VariantMultiHash()); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); static hifi::VariantMultiHash downloadMapping(const QString& url); private: - static void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it); + static void writeVariant(QBuffer& buffer, const hifi::VariantMultiHash::const_iterator& it); static hifi::VariantMultiHash parseMapping(QIODevice* device); static QHash _typesToNames; diff --git a/libraries/model-serializers/src/GLTFSerializer.cpp b/libraries/model-serializers/src/GLTFSerializer.cpp index 2c86c2c0904..981fcfc59ab 100644 --- a/libraries/model-serializers/src/GLTFSerializer.cpp +++ b/libraries/model-serializers/src/GLTFSerializer.cpp @@ -44,6 +44,7 @@ #include #include "FBXSerializer.h" +#include "shared/QtHelpers.h" float atof_locale_independent(char* str) { //TODO: Once we have C++17 we can use std::from_chars @@ -188,7 +189,7 @@ bool findAttribute(const QString &name, const cgltf_attribute *attributes, size_ return false; } -bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { +bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantMultiHash& mapping, const hifi::URL& url) { hfmModel.originalURL = url.toString(); int numNodes = (int)_data->nodes_count; @@ -999,7 +1000,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Build list of blendshapes from FST and model. typedef QPair WeightedIndex; - hifi::VariantMultiHash blendshapeMappings = mapping.value("bs").toHash(); + hifi::VariantMultiHash blendshapeMappings = qVariantToQMultiHash(mapping.value("bs")); QMultiHash blendshapeIndices; for (int i = 0;; ++i) { auto blendshapeName = QString(BLENDSHAPE_NAMES[i]); @@ -1198,7 +1199,7 @@ std::unique_ptr GLTFSerializer::getFactory() const { return std::make_unique>(); } -HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url) { +HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url) { _url = url; diff --git a/libraries/model-serializers/src/GLTFSerializer.h b/libraries/model-serializers/src/GLTFSerializer.h index 0786eac98b3..790ef1b0d00 100644 --- a/libraries/model-serializers/src/GLTFSerializer.h +++ b/libraries/model-serializers/src/GLTFSerializer.h @@ -32,7 +32,7 @@ class GLTFSerializer : public QObject, public HFMSerializer { MediaType getMediaType() const override; std::unique_ptr getFactory() const override; - HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; + HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url = hifi::URL()) override; ~GLTFSerializer(); private: cgltf_data* _data {nullptr}; @@ -43,7 +43,7 @@ class GLTFSerializer : public QObject, public HFMSerializer { bool getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); bool generateTargetData(cgltf_accessor *accessor, float weight, QVector& returnVector); - bool buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url); + bool buildGeometry(HFMModel& hfmModel, const hifi::VariantMultiHash& mapping, const hifi::URL& url); bool readBinary(const QString& url, cgltf_buffer &buffer); diff --git a/libraries/model-serializers/src/OBJSerializer.cpp b/libraries/model-serializers/src/OBJSerializer.cpp index 8529c72a338..1b4b11061b0 100644 --- a/libraries/model-serializers/src/OBJSerializer.cpp +++ b/libraries/model-serializers/src/OBJSerializer.cpp @@ -493,7 +493,7 @@ QNetworkReply* request(hifi::URL& url, bool isTest) { } -bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHash& mapping, HFMModel& hfmModel, +bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantMultiHash& mapping, HFMModel& hfmModel, float& scaleGuess, bool combineParts) { FaceGroup faces; HFMMesh& mesh = hfmModel.meshes[0]; @@ -666,7 +666,7 @@ std::unique_ptr OBJSerializer::getFactory() const { return std::make_unique>(); } -HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url) { +HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); QBuffer buffer { const_cast(&data) }; buffer.open(QIODevice::ReadOnly); diff --git a/libraries/model-serializers/src/OBJSerializer.h b/libraries/model-serializers/src/OBJSerializer.h index 20633e90e4c..b3ce01a2027 100644 --- a/libraries/model-serializers/src/OBJSerializer.h +++ b/libraries/model-serializers/src/OBJSerializer.h @@ -103,13 +103,13 @@ class OBJSerializer: public QObject, public HFMSerializer { // QObject so we can QString currentMaterialName; QHash materials; - HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url = hifi::URL()) override; + HFMModel::Pointer read(const hifi::ByteArray& data, const hifi::VariantMultiHash& mapping, const hifi::URL& url = hifi::URL()) override; private: hifi::URL _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHash& mapping, HFMModel& hfmModel, + bool parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantMultiHash& mapping, HFMModel& hfmModel, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const hifi::ByteArray& textureLine, hifi::ByteArray& filename, OBJMaterialTextureOptions& textureOptions); diff --git a/libraries/model-serializers/src/OBJWriter.cpp b/libraries/model-serializers/src/OBJWriter.cpp index dec35c7aac6..5a7c4cee806 100644 --- a/libraries/model-serializers/src/OBJWriter.cpp +++ b/libraries/model-serializers/src/OBJWriter.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include #include #include @@ -110,7 +112,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { graphics::Index partCount = (graphics::Index)mesh->getNumParts(); QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString::fromStdString(mesh->displayName)) - .replace(QRegExp("[^-_a-zA-Z0-9]"), "_"); + .replace(QRegularExpression("[^-_a-zA-Z0-9]"), "_"); for (int partIndex = 0; partIndex < partCount; partIndex++) { const graphics::Mesh::Part& part = partBuffer.get(partIndex); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 9454853418a..81f6200ff25 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -83,10 +83,8 @@ AccountManager::AccountManager(bool accountSettingsEnabled, UserAgentGetter user _accountSettingsEnabled(accountSettingsEnabled) { qRegisterMetaType("OAuthAccessToken"); - qRegisterMetaTypeStreamOperators("OAuthAccessToken"); qRegisterMetaType("DataServerAccountInfo"); - qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); qRegisterMetaType("QNetworkAccessManager::Operation"); qRegisterMetaType("JSONCallbackParameters"); @@ -347,7 +345,7 @@ void AccountManager::sendRequest(const QString& path, // double check if the finished network reply had a session ID in the header and make // sure that our session ID matches that value if so if (networkReply->hasRawHeader(METAVERSE_SESSION_ID_HEADER)) { - _sessionID = networkReply->rawHeader(METAVERSE_SESSION_ID_HEADER); + _sessionID = QUuid::fromBytes(networkReply->rawHeader(METAVERSE_SESSION_ID_HEADER)); } }); @@ -626,7 +624,7 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); + connect(requestReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } void AccountManager::requestAccessTokenWithOculus(const QString& nonce, const QString &oculusID) { @@ -649,7 +647,7 @@ void AccountManager::requestAccessTokenWithOculus(const QString& nonce, const QS QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); + connect(requestReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } void AccountManager::refreshAccessToken() { @@ -679,7 +677,7 @@ void AccountManager::refreshAccessToken() { QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::refreshAccessTokenFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(refreshAccessTokenError(QNetworkReply::NetworkError))); + connect(requestReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(refreshAccessTokenError(QNetworkReply::NetworkError))); } else { qCWarning(networking) << "Cannot refresh access token without refresh token." << "Access token will need to be manually refreshed."; @@ -807,7 +805,7 @@ void AccountManager::requestProfile() { QNetworkReply* profileReply = networkAccessManager.get(profileRequest); connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished); - connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); + connect(profileReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); } void AccountManager::requestProfileFinished() { @@ -857,7 +855,7 @@ void AccountManager::requestAccountSettings() { QNetworkReply* lockerReply = networkAccessManager.get(lockerRequest); connect(lockerReply, &QNetworkReply::finished, this, &AccountManager::requestAccountSettingsFinished); - connect(lockerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccountSettingsError(QNetworkReply::NetworkError))); + connect(lockerReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestAccountSettingsError(QNetworkReply::NetworkError))); _settings.startedLoading(); } @@ -935,7 +933,7 @@ void AccountManager::postAccountSettings() { QNetworkReply* lockerReply = networkAccessManager.put(lockerRequest, postData); connect(lockerReply, &QNetworkReply::finished, this, &AccountManager::postAccountSettingsFinished); - connect(lockerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(postAccountSettingsError(QNetworkReply::NetworkError))); + connect(lockerReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(postAccountSettingsError(QNetworkReply::NetworkError))); } void AccountManager::postAccountSettingsFinished() { diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 94ff1e72c38..cbe6380a3a9 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -33,6 +33,7 @@ #include "NetworkingConstants.h" #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" +#include const QString REDIRECT_HIFI_ADDRESS = NetworkingConstants::REDIRECT_HIFI_ADDRESS; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; @@ -280,7 +281,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrlIn, LookupTrigger trigger, c if (lookupUrl.host().isEmpty()) { // this was in the form hifi:/somewhere or hifi:somewhere. Fix it by making it hifi://somewhere - static const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_OVERTE + ":\\/{0,2}", Qt::CaseInsensitive); + static const QRegularExpression HIFI_SCHEME_REGEX = QRegularExpression(URL_SCHEME_OVERTE + ":\\/{0,2}", QRegularExpression::CaseInsensitiveOption); lookupUrl = QUrl(lookupUrl.toString().replace(HIFI_SCHEME_REGEX, URL_SCHEME_OVERTE + "://")); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index a78f9d7d73b..044790b175f 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -435,7 +435,7 @@ public slots: * * location.locationChangeRequired.connect(onLocationChangeRequired); */ - void locationChangeRequired(const glm::vec3& newPosition, + void locationChangeRequired(const glm::vec<3,float,glm::packed_highp>& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 5f87c31b865..36dbbaab9fb 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -68,7 +68,7 @@ void AssetClient::initCaching() { #ifdef Q_OS_ANDROID QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); #else - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); #endif _cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache"; } @@ -222,7 +222,7 @@ namespace { if (!dt.isValid()) { qDebug() << __FUNCTION__ << "unrecognized date format:" << dateString; } - dt.setTimeSpec(Qt::UTC); + dt.setTimeZone(QTimeZone::utc()); return dt; } QDateTime getHttpDateValue(const QVariantMap& headers, const QString& keyName, const QDateTime& defaultValue) { diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 5419424f46b..f5e34584bd2 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -12,6 +12,7 @@ #include "AssetResourceRequest.h" #include +#include #include #include diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index b0eb19bb735..7b64e2201e1 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -17,6 +17,7 @@ #include #include // for baseName #include +#include #include "NetworkAccessManager.h" #include "NetworkLogging.h" diff --git a/libraries/networking/src/BaseAssetScriptingInterface.cpp b/libraries/networking/src/BaseAssetScriptingInterface.cpp index 6dcc862c414..d5861316431 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.cpp +++ b/libraries/networking/src/BaseAssetScriptingInterface.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -262,7 +263,7 @@ Promise BaseAssetScriptingInterface::downloadBytes(QString hash) { { "data", data }, }; } else { - error = request->getError(); + error = QMetaEnum::fromType().valueToKey(request->getError()); result = { { "error", request->getError() } }; } // forward thread-safe copies back to our thread diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 5bb530d332b..3bd6785e3e1 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -110,11 +110,11 @@ QString FingerprintUtils::getMachineFingerprintString() { bool success = false; // try and open the key that contains the machine GUID - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) { + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) { DWORD type; DWORD guidSize; - const char* MACHINE_GUID_KEY = "MachineGuid"; + const wchar_t MACHINE_GUID_KEY[] = L"MachineGuid"; // try and retrieve the size of the GUID value if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, &type, NULL, &guidSize) == ERROR_SUCCESS) { diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index d182e7ea94a..87c3c1fe912 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -237,6 +237,12 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() { return *_dtlsSocket; } +PacketReceiver& LimitedNodeList::getPacketReceiver() { + Q_ASSERT(_packetReceiver); + return *_packetReceiver; +} + + #if defined(WEBRTC_DATA_CHANNELS) const WebRTCSocket* LimitedNodeList::getWebRTCSocket() { return _nodeSocket.getWebRTCSocket(); @@ -281,7 +287,7 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) { if (!hasBeenOutput) { sourcedVersionDebugSuppressMap.insert(sourceID, headerType); - senderString = uuidStringWithoutCurlyBraces(sourceID.toString()); + senderString = uuidStringWithoutCurlyBraces(sourceID); } } } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index cdb742a3c37..92822ae67d3 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -143,7 +143,7 @@ class LimitedNodeList : public QObject, public Dependency { #endif - PacketReceiver& getPacketReceiver() { return *_packetReceiver; } + PacketReceiver& getPacketReceiver(); virtual bool isDomainServer() const { return true; } virtual QUuid getDomainUUID() const { assert(false); return QUuid(); } diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 8a23c7dbd31..f29cbc60cc8 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -32,10 +32,10 @@ size_t std::hash::operator()(const NodePermissionsKey& key) } -NodePermissionsKey NodePermissions::standardNameLocalhost = NodePermissionsKey("localhost", 0); -NodePermissionsKey NodePermissions::standardNameLoggedIn = NodePermissionsKey("logged-in", 0); -NodePermissionsKey NodePermissions::standardNameAnonymous = NodePermissionsKey("anonymous", 0); -NodePermissionsKey NodePermissions::standardNameFriends = NodePermissionsKey("friends", 0); +NodePermissionsKey NodePermissions::standardNameLocalhost = NodePermissionsKey("localhost", QUuid()); +NodePermissionsKey NodePermissions::standardNameLoggedIn = NodePermissionsKey("logged-in", QUuid()); +NodePermissionsKey NodePermissions::standardNameAnonymous = NodePermissionsKey("anonymous", QUuid()); +NodePermissionsKey NodePermissions::standardNameFriends = NodePermissionsKey("friends", QUuid()); QStringList NodePermissions::standardNames = QList() << NodePermissions::standardNameLocalhost.first diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 5792a2c91aa..098976843bb 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -30,7 +30,7 @@ #include "NetworkLogging.h" #include "NodeList.h" -bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource, float priority) { +bool ResourceCacheSharedItems::appendRequest(std::weak_ptr resource, float priority) { Lock lock(_mutex); if ((uint32_t)_loadingRequests.size() < _requestLimit) { _loadingRequests.append({ resource, priority }); @@ -51,11 +51,11 @@ uint32_t ResourceCacheSharedItems::getRequestLimit() const { return _requestLimit; } -QList> ResourceCacheSharedItems::getPendingRequests() const { - QList> result; +QList> ResourceCacheSharedItems::getPendingRequests() const { + QList> result; Lock lock(_mutex); - foreach (QWeakPointer resource, _pendingRequests) { + foreach (std::weak_ptr resource, _pendingRequests) { auto locked = resource.lock(); if (locked) { result.append(locked); @@ -70,8 +70,8 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const { return _pendingRequests.size(); } -QList, float>> ResourceCacheSharedItems::getLoadingRequests() const { - QList, float>> result; +QList, float>> ResourceCacheSharedItems::getLoadingRequests() const { + QList, float>> result; Lock lock(_mutex); foreach(auto resourcePair, _loadingRequests) { @@ -89,16 +89,16 @@ uint32_t ResourceCacheSharedItems::getLoadingRequestsCount() const { return _loadingRequests.size(); } -void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { +void ResourceCacheSharedItems::removeRequest(std::weak_ptr resource) { Lock lock(_mutex); // resource can only be removed if it still has a ref-count, as // QWeakPointer has no operator== implementation for two weak ptrs, so // manually loop in case resource has been freed. for (int i = 0; i < _loadingRequests.size();) { - auto request = _loadingRequests.at(i).first; + auto request = _loadingRequests.at(i).first.lock(); // Clear our resource and any freed resources - if (!request || request.toStrongRef().data() == resource.toStrongRef().data()) { + if (!request || request.get() == resource.lock().get()) { _loadingRequests.removeAt(i); continue; } @@ -106,11 +106,11 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { } } -std::pair, float> ResourceCacheSharedItems::getHighestPendingRequest() { +std::pair, float> ResourceCacheSharedItems::getHighestPendingRequest() { // look for the highest priority pending request int highestIndex = -1; float highestPriority = -FLT_MAX; - QSharedPointer highestResource; + std::shared_ptr highestResource; Lock lock(_mutex); bool currentHighestIsFile = false; @@ -163,6 +163,7 @@ void ScriptableResourceCache::updateTotalSize(const qint64& deltaSize) { } ScriptableResource* ScriptableResourceCache::prefetch(const QUrl& url, void* extra, size_t extraHash) { + static_assert(sizeof(size_t) == sizeof(ulong)); return _resourceCache->prefetch(url, extra, extraHash); } @@ -224,11 +225,10 @@ ScriptableResource* ResourceCache::prefetchAndMoveToThread(const QUrl& url, void ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra, size_t extraHash) { ScriptableResource* result = nullptr; - if (QThread::currentThread() != thread()) { + if (QThread::currentThread() != this->thread()) { // Must be called in thread to ensure getResource returns a valid pointer - BLOCKING_INVOKE_METHOD(this, "prefetchAndMoveToThread", - Q_RETURN_ARG(ScriptableResource*, result), - Q_ARG(QUrl, url), Q_ARG(void*, extra), Q_ARG(size_t, extraHash), Q_ARG(QThread*, QThread::currentThread())); + QMetaObject::invokeMethod(this, &ResourceCache::prefetchAndMoveToThread, + qReturnArg(result), url, extra, extraHash, QThread::currentThread()); return result; } @@ -243,16 +243,16 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra, size_t result->finished(!resource->_failedToLoad); } else { result->_progressConnection = connect( - resource.data(), &Resource::onProgress, + resource.get(), &Resource::onProgress, result, &ScriptableResource::progressChanged); result->_loadingConnection = connect( - resource.data(), &Resource::loading, + resource.get(), &Resource::loading, result, &ScriptableResource::loadingChanged); result->_loadedConnection = connect( - resource.data(), &Resource::loaded, + resource.get(), &Resource::loaded, result, &ScriptableResource::loadedChanged); result->_finishedConnection = connect( - resource.data(), &Resource::finished, + resource.get(), &Resource::finished, result, &ScriptableResource::finished); } @@ -308,7 +308,7 @@ void ResourceCache::refreshAll() { clearUnusedResources(); resetUnusedResourceCounter(); - QHash>> allResources; + QHash>> allResources; { QReadLocker locker(&_resourcesLock); allResources = _resources; @@ -331,7 +331,7 @@ QVariantList ResourceCache::getResourceList() { if (QThread::currentThread() != thread()) { // NOTE: invokeMethod does not allow a const QObject* BLOCKING_INVOKE_METHOD(this, "getResourceList", - Q_RETURN_ARG(QVariantList, list)); + Q_GENERIC_RETURN_ARG(QVariantList, list)); } else { QList resources; { @@ -357,8 +357,8 @@ void ResourceCache::setRequestLimit(uint32_t limit) { } } -QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash) { - QSharedPointer resource; +std::shared_ptr ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash) { + std::shared_ptr resource; { QWriteLocker locker(&_resourcesLock); auto& resourcesWithExtraHash = _resources[url]; @@ -373,10 +373,9 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& resource = createResourceCopy(oldResource); resource->setExtra(extra); resource->setExtraHash(extraHash); - resource->setSelf(resource); resource->setCache(this); resource->moveToThread(qApp->thread()); - connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); + connect(resource.get(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); resourcesWithExtraHash.insert(extraHash, resource); resource->ensureLoading(); } @@ -394,10 +393,9 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& resource = createResource(url); resource->setExtra(extra); resource->setExtraHash(extraHash); - resource->setSelf(resource); resource->setCache(this); resource->moveToThread(qApp->thread()); - connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); + connect(resource.get(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); { QWriteLocker locker(&_resourcesLock); _resources[url].insert(extraHash, resource); @@ -416,7 +414,7 @@ void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) { resetUnusedResourceCounter(); } -void ResourceCache::addUnusedResource(const QSharedPointer& resource) { +void ResourceCache::addUnusedResource(const std::shared_ptr& resource) { // If it doesn't fit or its size is unknown, remove it from the cache. if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) { resource->setCache(nullptr); @@ -437,7 +435,7 @@ void ResourceCache::addUnusedResource(const QSharedPointer& resource) resetUnusedResourceCounter(); } -void ResourceCache::removeUnusedResource(const QSharedPointer& resource) { +void ResourceCache::removeUnusedResource(const std::shared_ptr& resource) { QWriteLocker locker(&_unusedResourcesLock); if (_unusedResources.contains(resource->getLRUKey())) { _unusedResources.remove(resource->getLRUKey()); @@ -453,7 +451,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) { while (!_unusedResources.empty() && _unusedResourcesSize + resourceSize > _unusedResourcesMaxSize) { // unload the oldest resource - QMap >::iterator it = _unusedResources.begin(); + QMap >::iterator it = _unusedResources.begin(); it.value()->setCache(nullptr); auto size = it.value()->getBytes(); @@ -472,7 +470,7 @@ void ResourceCache::clearUnusedResources() { // list on destruction, so keep clearing until there are no references left QWriteLocker locker(&_unusedResourcesLock); while (!_unusedResources.isEmpty()) { - foreach (const QSharedPointer& resource, _unusedResources) { + foreach (const std::shared_ptr& resource, _unusedResources) { resource->setCache(nullptr); } _unusedResources.clear(); @@ -525,7 +523,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) { emit dirty(); } -QList, float>> ResourceCache::getLoadingRequests() { +QList, float>> ResourceCache::getLoadingRequests() { return DependencyManager::get()->getLoadingRequests(); } @@ -537,8 +535,8 @@ uint32_t ResourceCache::getLoadingRequestCount() { return DependencyManager::get()->getLoadingRequestsCount(); } -bool ResourceCache::attemptRequest(QSharedPointer resource, float priority) { - Q_ASSERT(!resource.isNull()); +bool ResourceCache::attemptRequest(std::shared_ptr resource, float priority) { + Q_ASSERT(resource); auto sharedItems = DependencyManager::get(); if (sharedItems->appendRequest(resource, priority)) { @@ -548,7 +546,7 @@ bool ResourceCache::attemptRequest(QSharedPointer resource, float prio return false; } -void ResourceCache::requestCompleted(QWeakPointer resource) { +void ResourceCache::requestCompleted(std::weak_ptr resource) { auto sharedItems = DependencyManager::get(); sharedItems->removeRequest(resource); @@ -569,6 +567,7 @@ static int requestID = 0; Resource::Resource(const Resource& other) : QObject(), + std::enable_shared_from_this(), _url(other._url), _effectiveBaseURL(other._effectiveBaseURL), _activeUrl(other._activeUrl), @@ -583,6 +582,7 @@ Resource::Resource(const Resource& other) : _bytes(other._bytes), _requestID(++requestID), _extraHash(other._extraHash) { + // TODO: should we add an assert here to make sure this gets deleted on correct thread? if (!other._loaded) { _startedLoading = false; } @@ -597,11 +597,15 @@ Resource::Resource(const QUrl& url) : } Resource::~Resource() { +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + _wasDeleted = true; +#endif + Q_ASSERT(QThread::currentThread() == thread()); if (_request) { _request->disconnect(this); _request->deleteLater(); _request = nullptr; - ResourceCache::requestCompleted(_self); + ResourceCache::requestCompleted(weak_from_this()); } } @@ -643,13 +647,15 @@ void Resource::refresh() { _request->disconnect(this); _request->deleteLater(); _request = nullptr; - ResourceCache::requestCompleted(_self); + ResourceCache::requestCompleted(weak_from_this()); } _activeUrl = _url; init(); ensureLoading(); - emit onRefresh(); + if (!_isScheduledForDeletion) { + emit onRefresh(); + } } void Resource::allReferencesCleared() { @@ -660,8 +666,7 @@ void Resource::allReferencesCleared() { if (_cache && isCacheable()) { // create and reinsert new shared pointer - QSharedPointer self(this, &Resource::deleter); - setSelf(self); + std::shared_ptr self(this, Resource::sharedPtrDeleter); reinsert(); // add to the unused list @@ -673,6 +678,14 @@ void Resource::allReferencesCleared() { _cache->resetTotalResourceCounter(); } + if (_request) { + _request->disconnect(this); + _request->deleteLater(); + _request = nullptr; + ResourceCache::requestCompleted(weak_from_this()); + } + _isScheduledForDeletion = true; + disconnect(); deleteLater(); } } @@ -724,8 +737,11 @@ void Resource::attemptRequest() { << "- retrying asset load - attempt" << _attempts << " of " << MAX_ATTEMPTS; } - auto self = _self.lock(); + auto self = weak_from_this().lock(); if (self) { +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(!_wasDeleted); +#endif ResourceCache::attemptRequest(self); } } @@ -737,7 +753,9 @@ void Resource::finishedLoading(bool success) { } else { _failedToLoad = true; } - emit finished(success); + if (!_isScheduledForDeletion) { + emit finished(success); + } } void Resource::setSize(const qint64& bytes) { @@ -747,7 +765,7 @@ void Resource::setSize(const qint64& bytes) { void Resource::reinsert() { QWriteLocker locker(&_cache->_resourcesLock); - _cache->_resources[_url].insert(_extraHash, _self); + _cache->_resources[_url].insert(_extraHash, weak_from_this()); } @@ -764,7 +782,7 @@ void Resource::makeRequest() { this, _activeUrl, true, -1, "Resource::makeRequest"); if (!_request) { - ResourceCache::requestCompleted(_self); + ResourceCache::requestCompleted(weak_from_this()); finishedLoading(false); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; @@ -791,6 +809,15 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { + // Make sure we keep the Resource alive here + auto self = weak_from_this().lock(); + if (!self) { + // Make sure the resource wasn't deleted yet, and it's just scheduled for deletion or pointer has expired. +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(!_wasDeleted); +#endif + } + if (!_request || _request != sender()) { // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current" @@ -799,7 +826,10 @@ void Resource::handleReplyFinished() { { "from_cache", false }, { "size_mb", _bytesTotal / 1000000.0 } }); - ResourceCache::requestCompleted(_self); + // TODO: should we still emit requestCompleted if resource's shared_ptr has expired? + if (self) { + ResourceCache::requestCompleted(weak_from_this()); + } return; } @@ -808,9 +838,7 @@ void Resource::handleReplyFinished() { { "size_mb", _bytesTotal / 1000000.0 } }); - // Make sure we keep the Resource alive here - auto self = _self.lock(); - ResourceCache::requestCompleted(_self); + ResourceCache::requestCompleted(weak_from_this()); auto result = _request->getResult(); if (result == ResourceRequest::Success) { @@ -829,10 +857,16 @@ void Resource::handleReplyFinished() { } setSize(_bytesTotal); - emit loaded(data); - downloadFinished(data); + // TODO: should we emit these after pointer has expired or delete_later() was called? + if (self) { + emit loaded(data); + downloadFinished(data); + } } else { - handleFailedRequest(result); + // TODO: should we emit these after pointer has expired or delete_later() was called? + if (self) { + handleFailedRequest(result); + } } _request->disconnect(this); @@ -873,7 +907,9 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) { qCDebug(networking) << "Error loading:" << metaEnum.valueToKey(result) << "resource:" << _url.toString(); auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; - emit failed(error); + if (!_isScheduledForDeletion) { + emit failed(error); + } willRetry = false; finishedLoading(false); break; @@ -882,6 +918,6 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) { return willRetry; } -uint qHash(const QPointer& value, uint seed) { +size_t qHash(const QPointer& value, size_t seed) { return qHash(value.data(), seed); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index b4290402c7e..9a1c3b5c7eb 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -22,9 +22,7 @@ #include #include #include -#include #include -#include #include #include @@ -67,14 +65,14 @@ class ResourceCacheSharedItems : public Dependency { using Lock = std::unique_lock; public: - bool appendRequest(QWeakPointer newRequest, float priority); - void removeRequest(QWeakPointer doneRequest); + bool appendRequest(std::weak_ptr newRequest, float priority); + void removeRequest(std::weak_ptr doneRequest); void setRequestLimit(uint32_t limit); uint32_t getRequestLimit() const; - QList> getPendingRequests() const; - std::pair, float> getHighestPendingRequest(); + QList> getPendingRequests() const; + std::pair, float> getHighestPendingRequest(); uint32_t getPendingRequestsCount() const; - QList, float>> getLoadingRequests() const; + QList, float>> getLoadingRequests() const; uint32_t getLoadingRequestsCount() const; void clear(); @@ -82,8 +80,8 @@ class ResourceCacheSharedItems : public Dependency { ResourceCacheSharedItems() = default; mutable Mutex _mutex; - QList> _pendingRequests; - QList, float>> _loadingRequests; + QList> _pendingRequests; + QList, float>> _loadingRequests; const uint32_t DEFAULT_REQUEST_LIMIT = 10; uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT }; }; @@ -142,7 +140,7 @@ class ScriptableResource : public QObject { const QUrl& getURL() const { return _url; } int getState() const { return (int)_state; } - const QSharedPointer& getResource() const { return _resource; } + const std::shared_ptr& getResource() const { return _resource; } bool isInScript() const; void setInScript(bool isInScript); @@ -180,7 +178,7 @@ private slots: friend class ResourceCache; // Holds a ref to the resource to keep it in scope - QSharedPointer _resource; + std::shared_ptr _resource; QMetaObject::Connection _progressConnection; QMetaObject::Connection _loadingConnection; @@ -217,7 +215,7 @@ class ResourceCache : public QObject { void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } - static QList, float>> getLoadingRequests(); + static QList, float>> getLoadingRequests(); static uint32_t getPendingRequestCount(); static uint32_t getLoadingRequestCount(); @@ -246,8 +244,8 @@ protected slots: /// returns an empty smart pointer and loads its asynchronously. /// \param fallback a fallback URL to load if the desired one is unavailable // FIXME: std::numeric_limits::max() could be a valid extraHash - QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl()) { return getResource(url, fallback, nullptr, std::numeric_limits::max()); } - QSharedPointer getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash); + std::shared_ptr getResource(const QUrl& url, const QUrl& fallback = QUrl()) { return getResource(url, fallback, nullptr, std::numeric_limits::max()); } + std::shared_ptr getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash); private slots: void clearATPAssets(); @@ -266,16 +264,16 @@ private slots: Q_INVOKABLE ScriptableResource* prefetchAndMoveToThread(const QUrl& url, void* extra, size_t extraHash, QThread *scriptThread); /// Creates a new resource. - virtual QSharedPointer createResource(const QUrl& url) = 0; - virtual QSharedPointer createResourceCopy(const QSharedPointer& resource) = 0; + virtual std::shared_ptr createResource(const QUrl& url) = 0; + virtual std::shared_ptr createResourceCopy(const std::shared_ptr& resource) = 0; - void addUnusedResource(const QSharedPointer& resource); - void removeUnusedResource(const QSharedPointer& resource); + void addUnusedResource(const std::shared_ptr& resource); + void removeUnusedResource(const std::shared_ptr& resource); /// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading /// \return true if the resource began loading, otherwise false if the resource is in the pending queue - static bool attemptRequest(QSharedPointer resource, float priority = NAN); - static void requestCompleted(QWeakPointer resource); + static bool attemptRequest(std::shared_ptr resource, float priority = NAN); + static void requestCompleted(std::weak_ptr resource); static bool attemptHighestPriorityRequest(); private: @@ -290,7 +288,7 @@ private slots: void resetResourceCounters(); // Resources - QHash>> _resources; + QHash>> _resources; QReadWriteLock _resourcesLock { QReadWriteLock::Recursive }; int _lastLRUKey = 0; @@ -298,7 +296,7 @@ private slots: std::atomic _totalResourcesSize { 0 }; // Cached resources - QMap> _unusedResources; + QMap> _unusedResources; QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive }; qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; @@ -388,7 +386,8 @@ class ScriptableResourceCache : public QObject { Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr, std::numeric_limits::max()); } // FIXME: This function variation shouldn't be in the API. - Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, void* extra, size_t extraHash); + // QT6TODO: ulong is correct only on Linux. We should use size_t here, but then QT MOC will fail to process the function correctly. + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, void* extra, ulong extraHash); signals: @@ -412,7 +411,7 @@ class ScriptableResourceCache : public QObject { }; /// Base class for resources. -class Resource : public QObject { +class Resource : public QObject, public std::enable_shared_from_this { Q_OBJECT public: @@ -456,10 +455,9 @@ class Resource : public QObject { /// Refreshes the resource. virtual void refresh(); - void setSelf(const QWeakPointer& self) { _self = self; } - void setCache(ResourceCache* cache) { _cache = cache; } + static void sharedPtrDeleter(Resource *object) { object->deleter(); }; virtual void deleter() { allReferencesCleared(); } const QUrl& getURL() const { return _url; } @@ -524,6 +522,13 @@ protected slots: /// Return true if the resource will be retried virtual bool handleFailedRequest(ResourceRequest::Result result); + // Safeguard for debugging. +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + std::atomic _wasDeleted{ false }; +#endif + + std::atomic_bool _isScheduledForDeletion{ false }; + QUrl _url; QUrl _effectiveBaseURL { _url }; QUrl _activeUrl; @@ -538,7 +543,6 @@ protected slots: bool _loaded = false; QHash, std::function> _loadPriorityOperators; - QWeakPointer _self; QPointer _cache; qint64 _bytesReceived { 0 }; diff --git a/libraries/networking/src/SocketType.h b/libraries/networking/src/SocketType.h index 33da7c4ea03..30432eb2381 100644 --- a/libraries/networking/src/SocketType.h +++ b/libraries/networking/src/SocketType.h @@ -13,6 +13,7 @@ #ifndef overte_SocketType_h #define overte_SocketType_h +#include /// @addtogroup Networking /// @{ diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 8e4e6b8fb57..4b78cc5fa29 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -12,6 +12,8 @@ #include "UserActivityLoggerScriptingInterface.h" #include "UserActivityLogger.h" +#include + void UserActivityLoggerScriptingInterface::enabledEdit() { doLogAction("enabled_edit"); } diff --git a/libraries/networking/src/udt/NetworkSocket.cpp b/libraries/networking/src/udt/NetworkSocket.cpp index 298455e33ce..c846c648b22 100644 --- a/libraries/networking/src/udt/NetworkSocket.cpp +++ b/libraries/networking/src/udt/NetworkSocket.cpp @@ -8,6 +8,8 @@ #include "NetworkSocket.h" +#include + #include "../NetworkLogging.h" @@ -23,7 +25,7 @@ NetworkSocket::NetworkSocket(QObject* parent) : connect(&_udpSocket, &QUdpSocket::readyRead, this, &NetworkSocket::readyRead); connect(&_udpSocket, &QAbstractSocket::stateChanged, this, &NetworkSocket::onUDPStateChanged); // Use old SIGNAL/SLOT mechanism for Android builds. - connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), + connect(&_udpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(onUDPSocketError(QAbstractSocket::SocketError))); #if defined(WEBRTC_DATA_CHANNELS) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 2221c4bf2b0..d200be9e594 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -16,6 +16,7 @@ #include #include +#include #include diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 16b46d8c165..a43f72775f3 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -807,7 +807,13 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); result.resize(length); - memcpy(result.data(), dataBytes, length * sizeof(float)); + // It does a lot of zero-length writes to nullptr for some reason. + if (length != 0) { + // These were null sometimes, so it's best to check in debug builds. + Q_ASSERT(result.data()); + Q_ASSERT(dataBytes); + memcpy(result.data(), dataBytes, length * sizeof(float)); + } return sizeof(uint16_t) + length * sizeof(float); } @@ -834,7 +840,13 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto memcpy(&length, dataBytes, sizeof(uint16_t)); dataBytes += sizeof(length); result.resize(length); - memcpy(result.data(), dataBytes, length * sizeof(QUuid)); + // It does a lot of zero-length writes to nullptr for some reason. + if (length != 0) { + // These were null sometimes, so it's best to check in debug builds. + Q_ASSERT(result.data()); + Q_ASSERT(dataBytes); + memcpy(result.data(), dataBytes, length * sizeof(QUuid)); + } return sizeof(uint16_t) + length * sizeof(QUuid); } diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index a2c887ad5a8..852cc6e7175 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -155,10 +155,16 @@ int OctreeQuery::parseData(ReceivedMessage& message) { sourceBuffer += binaryParametersBytes; // grab the parameter object from the packed binary representation of JSON - auto newJsonDocument = QJsonDocument(QCborValue::fromCbor(binaryJSONParameters).toJsonValue().toObject()); - - QWriteLocker jsonParameterLocker { &_jsonParametersLock }; - _jsonParameters = newJsonDocument.object(); + QCborParserError cborError; + auto cbor = QCborValue::fromCbor(binaryJSONParameters, &cborError); + if (cborError.error == QCborError::NoError) { + auto newJsonDocument = QJsonDocument(cbor.toJsonValue().toObject()); + + QWriteLocker jsonParameterLocker { &_jsonParametersLock }; + _jsonParameters = newJsonDocument.object(); + } else { + qWarning() << "OctreeQuery::parseData: corrupted cbor " << QString(binaryJSONParameters.toHex()); + } } OctreeQueryFlags queryFlags; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e2b27f4b268..3ede2c41e1c 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -68,7 +68,7 @@ namespace gpu { } class NetworkTexture; -using NetworkTexturePointer = QSharedPointer; +using NetworkTexturePointer = std::shared_ptr; typedef struct __GLsync *GLsync; // Stereo display functionality diff --git a/libraries/plugins/src/plugins/InputConfiguration.cpp b/libraries/plugins/src/plugins/InputConfiguration.cpp index c206d67b66c..89e639e853c 100644 --- a/libraries/plugins/src/plugins/InputConfiguration.cpp +++ b/libraries/plugins/src/plugins/InputConfiguration.cpp @@ -24,7 +24,7 @@ QStringList InputConfiguration::inputPlugins() { if (QThread::currentThread() != thread()) { QStringList result; BLOCKING_INVOKE_METHOD(this, "inputPlugins", - Q_RETURN_ARG(QStringList, result)); + Q_GENERIC_RETURN_ARG(QStringList, result)); return result; } @@ -46,7 +46,7 @@ QStringList InputConfiguration::activeInputPlugins() { if (QThread::currentThread() != thread()) { QStringList result; BLOCKING_INVOKE_METHOD(this, "activeInputPlugins", - Q_RETURN_ARG(QStringList, result)); + Q_GENERIC_RETURN_ARG(QStringList, result)); return result; } @@ -69,8 +69,8 @@ QString InputConfiguration::configurationLayout(QString pluginName) { if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "configurationLayout", - Q_RETURN_ARG(QString, result), - Q_ARG(QString, pluginName)); + Q_GENERIC_RETURN_ARG(QString, result), + Q_GENERIC_ARG(QString, pluginName)); return result; } @@ -86,8 +86,8 @@ QString InputConfiguration::configurationLayout(QString pluginName) { void InputConfiguration::setConfigurationSettings(QJsonObject configurationSettings, QString pluginName) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "setConfigurationSettings", - Q_ARG(QJsonObject, configurationSettings), - Q_ARG(QString, pluginName)); + Q_GENERIC_ARG(QJsonObject, configurationSettings), + Q_GENERIC_ARG(QString, pluginName)); return; } @@ -102,8 +102,8 @@ QJsonObject InputConfiguration::configurationSettings(QString pluginName) { if (QThread::currentThread() != thread()) { QJsonObject result; BLOCKING_INVOKE_METHOD(this, "configurationSettings", - Q_RETURN_ARG(QJsonObject, result), - Q_ARG(QString, pluginName)); + Q_GENERIC_RETURN_ARG(QJsonObject, result), + Q_GENERIC_ARG(QString, pluginName)); return result; } @@ -133,7 +133,7 @@ bool InputConfiguration::uncalibratePlugin(QString pluginName) { if (QThread::currentThread() != thread()) { bool result; BLOCKING_INVOKE_METHOD(this, "uncalibratePlugin", - Q_ARG(bool, result)); + Q_GENERIC_ARG(bool, result)); return result; } diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index 0925efa0561..b5d98c36d4d 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -804,15 +804,19 @@ std::pair> NetworkMaterialResource } NetworkMaterialResourcePointer MaterialCache::getMaterial(const QUrl& url) { - return ResourceCache::getResource(url).staticCast(); + auto networkMaterialResource = std::dynamic_pointer_cast(getResource(url)); + Q_ASSERT(networkMaterialResource); + return networkMaterialResource; } -QSharedPointer MaterialCache::createResource(const QUrl& url) { - return QSharedPointer(new NetworkMaterialResource(url), &Resource::deleter); +std::shared_ptr MaterialCache::createResource(const QUrl& url) { + return std::shared_ptr(new NetworkMaterialResource(url), Resource::sharedPtrDeleter); } -QSharedPointer MaterialCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new NetworkMaterialResource(*resource.staticCast()), &Resource::deleter); +std::shared_ptr MaterialCache::createResourceCopy(const std::shared_ptr& resource) { + auto networkMaterialResource = std::dynamic_pointer_cast(resource); + Q_ASSERT(networkMaterialResource); + return std::shared_ptr(new NetworkMaterialResource(*networkMaterialResource), Resource::sharedPtrDeleter); } NetworkMaterial::NetworkMaterial(const NetworkMaterial& m) : @@ -1130,13 +1134,17 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { bool NetworkMaterial::isMissingTexture() { for (auto& networkTexture : _textures) { - auto& texture = networkTexture.second.texture; + auto texture = networkTexture.second.texture; if (!texture) { continue; } // Failed texture downloads need to be considered as 'loaded' // or the object will never fade in - bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined()); + auto gpuTexture = texture->getGPUTexture(); + if (gpuTexture) { + Q_ASSERT(!gpuTexture->wasDeleted); + } + bool finished = texture->isFailed() || (texture->isLoaded() && gpuTexture && gpuTexture->isDefined()); if (!finished) { return true; } diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index 90d2a6964dc..f9e1c6f4cdb 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -51,6 +51,7 @@ class NetworkMaterial : public graphics::Material { } }; using Textures = std::unordered_map; + // TODO: investigate if this could pass reference instead Textures getTextures() { return _textures; } protected: @@ -202,6 +203,7 @@ class NetworkMaterialResource : public Resource { public: NetworkMaterialResource() : Resource() {} NetworkMaterialResource(const QUrl& url); + virtual ~NetworkMaterialResource() {} QString getType() const override { return "NetworkMaterial"; } @@ -227,7 +229,7 @@ class NetworkMaterialResource : public Resource { static std::pair> parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl = QUrl()); }; -using NetworkMaterialResourcePointer = QSharedPointer; +using NetworkMaterialResourcePointer = std::shared_ptr; using MaterialMapping = std::vector>; Q_DECLARE_METATYPE(MaterialMapping) @@ -239,8 +241,8 @@ class MaterialCache : public ResourceCache, public Dependency { NetworkMaterialResourcePointer getMaterial(const QUrl& url); protected: - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; }; #endif diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.h b/libraries/procedural/src/procedural/ReferenceMaterial.h index ce58e811c02..04bece16fd9 100644 --- a/libraries/procedural/src/procedural/ReferenceMaterial.h +++ b/libraries/procedural/src/procedural/ReferenceMaterial.h @@ -77,7 +77,7 @@ class ReferenceMaterial : public graphics::ProceduralMaterial { private: static std::function _unboundMaterialForUUIDOperator; std::function _materialForUUIDOperator; - mutable bool _locked { false }; + mutable std::atomic _locked { false }; graphics::MaterialPointer getMaterial() const; std::shared_ptr getNetworkMaterial() const; diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index aabe6f36ba5..a3a6ed5788f 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -10,6 +10,11 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif //Q_OS_WIN +#include + #include #include #include @@ -35,6 +40,9 @@ using namespace hifi::qml; using namespace hifi::qml::impl; +// QT6TODO: Use one shared virtual pointing device, where do we put it though? +static std::shared_ptr _touchDevice; + QmlUrlValidator OffscreenSurface::validator = [](const QUrl& url) -> bool { if (url.isRelative()) { return true; @@ -92,12 +100,24 @@ void OffscreenSurface::setSharedContext(QOpenGLContext* sharedContext) { std::function OffscreenSurface::getDiscardLambda() { return [](uint32_t texture, void* fence) { - SharedObject::getTextureCache().releaseTexture({ texture, static_cast(fence) }); + SharedObject::getTextureCache().releaseTexture(TextureCache::Value(texture, static_cast(fence))); }; } OffscreenSurface::OffscreenSurface() : _sharedObject(new impl::SharedObject()) { + static std::once_flag once; + std::call_once(once, [&] { + _touchDevice = std::make_shared( + "OffscreenSurfacePointingDevice", + 1, + QInputDevice::DeviceType::AllDevices, + QPointingDevice::PointerType::AllPointerTypes, + QInputDevice::Capability::All, + 4, //maxPoints + 2 // buttonCount + ); + }); } OffscreenSurface::~OffscreenSurface() { @@ -178,7 +198,7 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) QWheelEvent mappedEvent(transformedPos, wheelEvent->globalPosition(), wheelEvent->pixelDelta(), wheelEvent->angleDelta(), wheelEvent->buttons(), wheelEvent->modifiers(), wheelEvent->phase(), - wheelEvent->inverted(), wheelEvent->source()); + wheelEvent->inverted(), wheelEvent->source(), _touchDevice.get()); mappedEvent.ignore(); if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) { @@ -186,19 +206,22 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) } break; } - case QEvent::MouseMove: { + // QT6TODO: this may be not necessary, mouse move events were doubled with it and without it nothing seems to break. + /*case QEvent::MouseMove: { QMouseEvent* mouseEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); - QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(), - mouseEvent->buttons(), mouseEvent->modifiers()); + QPointF transformedPos = mapToVirtualScreen(mouseEvent->position()); + QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->globalPosition(), mouseEvent->button(), + mouseEvent->buttons(), mouseEvent->modifiers(), + _touchDevice.get()); mappedEvent.ignore(); if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) { return mappedEvent.isAccepted(); } break; - } + }*/ #if defined(Q_OS_ANDROID) + // QT6TODO: Cases above needed to be deleted for Qt6 due to doubled events, so maybe these are not needed too? case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: { @@ -223,7 +246,7 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) Q_UNREACHABLE(); } // Same case as OffscreenUi.cpp::eventFilter: touch events are always being accepted so we now use mouse events and consider one touch, touchPoints()[0]. - QMouseEvent fakeMouseEvent(fakeMouseEventType, originalEvent->touchPoints()[0].pos(), fakeMouseButton, fakeMouseButtons, Qt::NoModifier); + QMouseEvent fakeMouseEvent(fakeMouseEventType, originalEvent->touchPoints()[0].pos(), fakeMouseButton, fakeMouseButtons, Qt::NoModifier, _touchDevice.get()); fakeMouseEvent.ignore(); if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &fakeMouseEvent)) { /*qInfo() << __FUNCTION__ << "sent fake touch event:" << fakeMouseEvent.type() diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp index c2e69d5a9b2..03d46148643 100644 --- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include "Profiling.h" @@ -60,7 +61,11 @@ RenderEventHandler::RenderEventHandler(SharedObject* shared, QThread* targetThre moveToThread(targetThread); } +// I'm not sure if several contexts can be initalized with the same shared context concurrently, so better safe than sorry. +static std::mutex renderControlInitMutex; + void RenderEventHandler::onInitalize() { + std::lock_guard lock(renderControlInitMutex); if (_shared->isQuit()) { return; } @@ -129,6 +134,10 @@ void RenderEventHandler::qmlRender(bool sceneGraphSync) { PROFILE_RANGE(render_qml_gl, __FUNCTION__); gl::globalLock(); + + _shared->_renderControl->polishItems(); + _shared->_renderControl->beginFrame(); + if (!_shared->preRender(sceneGraphSync)) { gl::globalRelease(); return; @@ -146,17 +155,10 @@ void RenderEventHandler::qmlRender(bool sceneGraphSync) { glClear(GL_COLOR_BUFFER_BIT); } else { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - _shared->setRenderTarget(_fbo, _currentSize); - - // workaround for https://highfidelity.atlassian.net/browse/BUGZ-1119 - { - // Serialize QML rendering because of a crash caused by Qt bug - // https://bugreports.qt.io/browse/QTBUG-77469 - static std::mutex qmlRenderMutex; - std::unique_lock qmlRenderLock{ qmlRenderMutex }; - _shared->_renderControl->render(); - } + _shared->setRenderTarget(texture, _currentSize); + _shared->_renderControl->render(); } + _shared->_renderControl->endFrame(); _shared->_lastRenderTime = usecTimestampNow(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, texture); @@ -166,7 +168,7 @@ void RenderEventHandler::qmlRender(bool sceneGraphSync) { // Fence will be used in another thread / context, so a flush is required glFlush(); _shared->updateTextureAndFence({ texture, fence }); - _shared->_quickWindow->resetOpenGLState(); + QQuickOpenGLUtils::resetOpenGLState(); } gl::globalRelease(); } diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 555389c4c3a..202e47f3759 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include @@ -68,11 +70,15 @@ SharedObject::SharedObject() { // NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events // NOTE: Must be created on the rendering thread or it will refuse to render, // so we wait until after its ctor to move object/context to this thread. - QQuickWindow::setDefaultAlphaBuffer(true); + // QT6TODO: QRhi fails to initialize with setDefaultAlphaBuffer + //QQuickWindow::setDefaultAlphaBuffer(true); _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setSurfaceType(QQuickWindow::OpenGLSurface); _quickWindow->setFormat(getDefaultOpenGLSurfaceFormat()); _quickWindow->setColor(Qt::transparent); - _quickWindow->setClearBeforeRendering(true); + // QT6TODO: setClearBeforeRendering was removed, what to do about this? + // https://doc.qt.io/qt-6/quick-changes-qt6.html + //_quickWindow->setClearBeforeRendering(true); #endif @@ -113,6 +119,10 @@ SharedObject::~SharedObject() { #ifndef DISABLE_QML if (_quickWindow) { +#ifdef ENABLE_SHARED_OBJECT_EVENT_DEBUG + // Remove event filter that was installed if event debugging is enabled. + _quickWindow->removeEventFilter(&_eventDebugFilter); +#endif _quickWindow->destroy(); delete _quickWindow; _quickWindow = nullptr; @@ -290,9 +300,15 @@ void SharedObject::initializeRenderControl(QOpenGLContext* context) { qFatal("QML rendering context has no share context"); } + Q_ASSERT(context->isValid()); + #ifndef DISABLE_QML if (!nsightActive()) { - _renderControl->initialize(context); + _quickWindow->setFormat(context->format()); + _quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context)); + bool result = _renderControl->initialize(); + Q_ASSERT(result); + Q_UNUSED(result); } #endif } @@ -308,9 +324,9 @@ void SharedObject::releaseTextureAndFence() { #endif } -void SharedObject::setRenderTarget(uint32_t fbo, const QSize& size) { +void SharedObject::setRenderTarget(uint32_t texture, const QSize& size) { #ifndef DISABLE_QML - _quickWindow->setRenderTarget(fbo, size); + _quickWindow->setRenderTarget(QQuickRenderTarget::fromOpenGLTexture(texture, size)); #endif } @@ -439,6 +455,11 @@ void SharedObject::wake() { void SharedObject::onInitialize() { #ifndef DISABLE_QML +#ifdef ENABLE_SHARED_OBJECT_EVENT_DEBUG + // Install event filter if event debugging is enabled. + _quickWindow->installEventFilter(&_eventDebugFilter); +#endif + // Associate root item with the window. _rootItem->setParentItem(_quickWindow->contentItem()); _renderControl->prepareThread(_renderThread); @@ -535,3 +556,20 @@ void SharedObject::resume() { bool SharedObject::isPaused() const { return _paused; } + +#ifdef ENABLE_SHARED_OBJECT_EVENT_DEBUG +bool SharedObjectEventDebug::eventFilter(QObject *object, QEvent *event) { + QMouseEvent *mouseEvent = dynamic_cast(event); + if (mouseEvent) { + if (mouseEvent->device()->name() == "WebEntityMouseDevice") { + qDebug() << "SharedObjectEventDebug QMouseEevent: " << mouseEvent << mouseEvent->buttons(); + } + //qDebug() << "SharedObjectEventDebug QMouseEevent: " << mouseEvent << mouseEvent->buttons(); + } + QTouchEvent *touchEvent = dynamic_cast(event); + if (touchEvent) { + qDebug() << "SharedObjectEventDebug QTouchEvent: " << touchEvent; + } + return false; +}; +#endif diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h index 4f287471e82..90178e5eb22 100644 --- a/libraries/qml/src/qml/impl/SharedObject.h +++ b/libraries/qml/src/qml/impl/SharedObject.h @@ -32,6 +32,27 @@ namespace impl { class RenderControl; class RenderEventHandler; +// Uncomment to show events received by the shared object. +//#define ENABLE_SHARED_OBJECT_EVENT_DEBUG + +/** + * @brief A class used for debugging input events being sent to shared object. + * + **/ + +#ifdef ENABLE_SHARED_OBJECT_EVENT_DEBUG +class SharedObjectEventDebug : public QObject { + Q_OBJECT +public: + bool eventFilter(QObject *object, QEvent *event); +}; +#endif + +/** + * @brief A class containing virtual Qt window for webentiies and in-game overlay. + * + **/ + class SharedObject : public QObject { Q_OBJECT @@ -73,7 +94,9 @@ class SharedObject : public QObject { // Called by the render event handler, from the render thread void initializeRenderControl(QOpenGLContext* context); void releaseTextureAndFence(); - void setRenderTarget(uint32_t fbo, const QSize& size); + + // NOTE: On Qt5 this took an FBO handle, on Qt6 it takes a texture handle + void setRenderTarget(uint32_t texture, const QSize& size); QQmlEngine* acquireEngine(OffscreenSurface* surface); void releaseEngine(QQmlEngine* engine); @@ -98,6 +121,10 @@ class SharedObject : public QObject { mutable QMutex _mutex; QWaitCondition _cond; +#ifdef ENABLE_SHARED_OBJECT_EVENT_DEBUG + SharedObjectEventDebug _eventDebugFilter; +#endif + #ifndef DISABLE_QML QWindow* _proxyWindow { nullptr }; RenderControl* _renderControl { nullptr }; diff --git a/libraries/recording/src/recording/ClipCache.cpp b/libraries/recording/src/recording/ClipCache.cpp index 285b5b7acff..933eb018e97 100644 --- a/libraries/recording/src/recording/ClipCache.cpp +++ b/libraries/recording/src/recording/ClipCache.cpp @@ -47,18 +47,23 @@ NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) { if (QThread::currentThread() != thread()) { NetworkClipLoaderPointer result; BLOCKING_INVOKE_METHOD(this, "getClipLoader", - Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url)); + Q_GENERIC_RETURN_ARG(NetworkClipLoaderPointer, result), Q_GENERIC_ARG(const QUrl&, url)); return result; } - return getResource(url).staticCast(); + auto clipLoader = std::dynamic_pointer_cast(getResource(url)); + Q_ASSERT(clipLoader); + + return clipLoader; } -QSharedPointer ClipCache::createResource(const QUrl& url) { +std::shared_ptr ClipCache::createResource(const QUrl& url) { qCDebug(recordingLog) << "Loading recording at" << url; - return QSharedPointer(new NetworkClipLoader(url), &Resource::deleter); + return std::shared_ptr(new NetworkClipLoader(url), Resource::sharedPtrDeleter); } -QSharedPointer ClipCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new NetworkClipLoader(*resource.staticCast()), &Resource::deleter); +std::shared_ptr ClipCache::createResourceCopy(const std::shared_ptr& resource) { + auto clipLoader = std::dynamic_pointer_cast(resource); + Q_ASSERT(clipLoader); + return std::shared_ptr(new NetworkClipLoader(*clipLoader), Resource::sharedPtrDeleter); } \ No newline at end of file diff --git a/libraries/recording/src/recording/ClipCache.h b/libraries/recording/src/recording/ClipCache.h index e909af7e70a..1d780be8da8 100644 --- a/libraries/recording/src/recording/ClipCache.h +++ b/libraries/recording/src/recording/ClipCache.h @@ -36,6 +36,7 @@ class NetworkClipLoader : public Resource { public: NetworkClipLoader(const QUrl& url); NetworkClipLoader(const NetworkClipLoader& other) : Resource(other), _clip(other._clip) {} + virtual ~NetworkClipLoader() {} virtual void downloadFinished(const QByteArray& data) override; ClipPointer getClip() { return _clip; } @@ -48,7 +49,7 @@ class NetworkClipLoader : public Resource { const NetworkClip::Pointer _clip; }; -using NetworkClipLoaderPointer = QSharedPointer; +using NetworkClipLoaderPointer = std::shared_ptr; class ClipCache : public ResourceCache, public Dependency { Q_OBJECT @@ -58,8 +59,8 @@ public slots: NetworkClipLoaderPointer getClipLoader(const QUrl& url); protected: - virtual QSharedPointer createResource(const QUrl& url) override; - QSharedPointer createResourceCopy(const QSharedPointer& resource) override; + virtual std::shared_ptr createResource(const QUrl& url) override; + std::shared_ptr createResourceCopy(const std::shared_ptr& resource) override; private: ClipCache(QObject* parent = nullptr); diff --git a/libraries/recording/src/recording/RecordingScriptingInterface.cpp b/libraries/recording/src/recording/RecordingScriptingInterface.cpp index a50453bb697..68f6f55e28e 100644 --- a/libraries/recording/src/recording/RecordingScriptingInterface.cpp +++ b/libraries/recording/src/recording/RecordingScriptingInterface.cpp @@ -90,7 +90,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script // hold a strong pointer to the loading clip so that it has a chance to load _clipLoaders.insert(clipLoader); - auto weakClipLoader = clipLoader.toWeakRef(); + std::weak_ptr weakClipLoader = clipLoader; auto manager = callback.engine()->manager(); if (!manager) { @@ -99,10 +99,11 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script } // when clip loaded, call the callback with the URL and success boolean - connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, manager, + connect(clipLoader.get(), &recording::NetworkClipLoader::clipLoaded, manager, [this, weakClipLoader, url, callback]() mutable { - if (auto clipLoader = weakClipLoader.toStrongRef()) { + if (auto clipLoader = weakClipLoader.lock()) { + Q_ASSERT(clipLoader); qCDebug(scriptengine) << "Loaded recording from" << url; playClip(clipLoader, url, callback); @@ -113,7 +114,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script }); // when clip load fails, call the callback with the URL and failure boolean - connect(clipLoader.data(), &recording::NetworkClipLoader::failed, manager, + connect(clipLoader.get(), &recording::NetworkClipLoader::failed, manager, [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable { qCDebug(scriptengine) << "Failed to load recording from\"" << url << '"'; @@ -123,7 +124,8 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script callback.call(ScriptValue(), args); } - if (auto clipLoader = weakClipLoader.toStrongRef()) { + if (auto clipLoader = weakClipLoader.lock()) { + Q_ASSERT(clipLoader); // drop out strong pointer to this clip so it is cleaned up _clipLoaders.remove(clipLoader); } @@ -151,7 +153,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { void RecordingScriptingInterface::setPlayerTime(float time) { if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setPlayerTime", Q_ARG(float, time)); + BLOCKING_INVOKE_METHOD(this, "setPlayerTime", Q_GENERIC_ARG(float, time)); return; } diff --git a/libraries/recording/src/recording/impl/PointerClip.cpp b/libraries/recording/src/recording/impl/PointerClip.cpp index 3a08dcf0e88..b1be4461cb0 100644 --- a/libraries/recording/src/recording/impl/PointerClip.cpp +++ b/libraries/recording/src/recording/impl/PointerClip.cpp @@ -107,7 +107,13 @@ void PointerClip::init(uchar* data, size_t size) { QByteArray fileHeaderData((char*)_data + fileHeaderFrameHeader.fileOffset, fileHeaderFrameHeader.size); - _header = QJsonDocument(QCborValue::fromCbor(fileHeaderData).toJsonValue().toObject()); + QCborParserError cborError; + auto cbor = QCborValue::fromCbor(fileHeaderData, &cborError); + if (cborError.error != QCborError::NoError) { + qWarning() << "PointerClip::init: corrupted cbor " << QString(fileHeaderData.toHex()); + return; + } + _header = QJsonDocument(cbor.toJsonValue().toObject()); } // Check for compression diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 971d09fbb65..8ece39525ed 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -5,7 +5,7 @@ set(TARGET_NAME render-utils) generate_render_pipelines() # pull in the resources.qrc file -qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") +qt6_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Gui Network Qml Quick) target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils/src") target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/libraries/render-utils/src") diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 9debb203ec8..b555c45fa74 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -248,7 +248,7 @@ class DebugAmbientOcclusionConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) - Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) public: DebugAmbientOcclusionConfig() : render::Job::Config(false) {} diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index b99e96a31cb..d414bc83a27 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -146,7 +146,7 @@ class AntialiasingConfig : public render::Job::Config { Q_PROPERTY(bool fxaaOnOff READ debugFXAA WRITE setDebugFXAA NOTIFY dirty) Q_PROPERTY(float debugShowVelocityThreshold MEMBER debugShowVelocityThreshold NOTIFY dirty) Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) - Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) Q_PROPERTY(float debugOrbZoom MEMBER debugOrbZoom NOTIFY dirty) Q_PROPERTY(bool showClosestFragment MEMBER showClosestFragment NOTIFY dirty) diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index 70a33b27eba..517218c8cd9 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -28,10 +28,10 @@ class HazeStageSetup : public render::StageSetup { class FetchHazeConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(glm::vec3 hazeColor MEMBER hazeColor WRITE setHazeColor NOTIFY dirty); + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> hazeColor MEMBER hazeColor WRITE setHazeColor NOTIFY dirty); Q_PROPERTY(float hazeGlareAngle MEMBER hazeGlareAngle WRITE setHazeGlareAngle NOTIFY dirty); - Q_PROPERTY(glm::vec3 hazeGlareColor MEMBER hazeGlareColor WRITE setHazeGlareColor NOTIFY dirty); + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> hazeGlareColor MEMBER hazeGlareColor WRITE setHazeGlareColor NOTIFY dirty); Q_PROPERTY(float hazeBaseReference MEMBER hazeBaseReference WRITE setHazeBaseReference NOTIFY dirty); Q_PROPERTY(bool isHazeActive MEMBER isHazeActive WRITE setHazeActive NOTIFY dirty); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9784ba13460..63345dd73bf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1399,7 +1399,7 @@ QStringList Model::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; BLOCKING_INVOKE_METHOD(const_cast(this), "getJointNames", - Q_RETURN_ARG(QStringList, result)); + Q_GENERIC_RETURN_ARG(QStringList, result)); return result; } return isLoaded() ? getHFMModel().getJointNames() : QStringList(); @@ -1783,7 +1783,7 @@ void Model::applyMaterialMapping() { if (networkMaterialResource->isLoaded()) { materialLoaded(); } else { - connect(networkMaterialResource.data(), &Resource::finished, materialLoaded); + connect(networkMaterialResource.get(), &Resource::finished, materialLoaded); } } } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index cf934f6023b..e72d9cbd8fd 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -164,10 +164,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { material->resetOpacityMap(); - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialAlbedo, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ALBEDO_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -188,10 +190,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialMetallic, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::METALLIC_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -210,10 +214,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialRoughness, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ROUGHNESS_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -232,10 +238,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialNormal, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::NORMAL_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -254,10 +262,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialOcclusion, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::OCCLUSION_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -276,10 +286,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialScattering, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::SCATTERING_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -299,10 +311,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::EMISSIVE_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -324,10 +338,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::LIGHT_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::LIGHT_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -345,10 +361,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::SPLAT_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - multiMaterial.setSplatMap(itr->second->getTextureView()._texture); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + multiMaterial.setSplatMap(textureView._texture); multiMaterial.addSamplerFunc([=]() { material->applySampler(graphics::MaterialKey::SPLAT_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -430,10 +448,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { material->resetOpacityMap(); - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialAlbedo, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ALBEDO_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -454,10 +474,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialNormal, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::NORMAL_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -477,10 +499,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialEmissiveLightmap, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::EMISSIVE_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -501,10 +525,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find(graphics::MaterialKey::SPLAT_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - multiMaterial.setSplatMap(itr->second->getTextureView()._texture); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + multiMaterial.setSplatMap(textureView._texture); multiMaterial.addSamplerFunc([=]() { material->applySampler(graphics::MaterialKey::SPLAT_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -576,10 +602,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADE_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialShade, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialShade, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::SHADE_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -598,10 +626,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::SHADING_SHIFT_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialShadingShift, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialShadingShift, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::SHADING_SHIFT_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -620,10 +650,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::MATCAP_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialMatcap, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialMatcap, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::MATCAP_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -642,10 +674,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::RIM_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialRim, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialRim, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::RIM_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { @@ -664,10 +698,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto itr = textureMaps.find((graphics::Material::MapChannel) NetworkMToonMaterial::UV_ANIMATION_MASK_MAP); if (itr != textureMaps.end()) { if (itr->second->isDefined()) { - drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialUVAnimationMask, itr->second->getTextureView()); + auto textureView = itr->second->getTextureView(); + Q_ASSERT(textureView); + drawMaterialTextures[layerIndex]->setTexture(gr::Texture::MaterialUVAnimationMask, textureView); multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::UV_ANIMATION_MASK_MAP); }); - if (itr->second->getTextureView().isReference()) { - multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + if (textureView.isReference()) { + multiMaterial.addReferenceTexture(textureView.getTextureOperator()); } wasSet = true; } else { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 992de467ec7..514816a0d5a 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -59,7 +59,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende const auto currentKeyLight = setupOutput.getN(4); // Fetch and cull the items from the scene - static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withOpaque().withoutLayered().withTagBits(tagBits, tagMask); + static const ItemFilter shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withOpaque().withoutLayered().withTagBits(tagBits, tagMask); const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying(); const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 710a3f63e1b..cfb2e893660 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -111,6 +111,13 @@ int readHelper(void* dst, int length, void* data) { if (_readOffset.localData() + length > _readMax.localData()) { return -1; } + // Sometimes artery_font::decode issues zero-length reads to nullptr, which causes issues with memory sanitizers. + if (length == 0) { + return 0; + } + // These were null sometimes, so it's best to check in debug builds. + Q_ASSERT(dst); + Q_ASSERT(data); memcpy(dst, (char *)data + _readOffset.localData(), length); _readOffset.setLocalData(_readOffset.localData() + length); return length; @@ -152,7 +159,7 @@ void Font::read(QIODevice& in) { auto& g = arteryFont.variants[0].glyphs[i]; Glyph glyph; - glyph.c = g.codepoint; + glyph.c = QChar(g.codepoint); glyph.texOffset = glm::vec2(g.imageBounds.l, g.imageBounds.b); glyph.texSize = glm::vec2(g.imageBounds.r, g.imageBounds.t) - glyph.texOffset; glyph.offset = glm::vec2(g.planeBounds.l, g.planeBounds.b); diff --git a/libraries/script-engine/src/CanvasCommand.h b/libraries/script-engine/src/CanvasCommand.h index 838af044f22..b6022577af4 100644 --- a/libraries/script-engine/src/CanvasCommand.h +++ b/libraries/script-engine/src/CanvasCommand.h @@ -417,7 +417,8 @@ struct CanvasCommand { static CanvasCommand fillRect(const QRectF& rect) { CanvasCommand cmd; cmd.kind = FillRect; - return CanvasCommand { .kind = FillRect, ._rect = rect }; + cmd._rect = rect; + return cmd; } static CanvasCommand fillEllipse(const QRectF& rect) { diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 8c56f513ab8..73cbc11458e 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -53,7 +53,7 @@ public slots: * @param {Mat4} m2 - The second matrix. * @returns {Mat4} m1 multiplied with m2. */ - glm::mat4 multiply(const glm::mat4& m1, const glm::mat4& m2) const; + glm::mat<4,4,float,glm::packed_highp> multiply(const glm::mat<4,4,float,glm::packed_highp>& m1, const glm::mat<4,4,float,glm::packed_highp>& m2) const; /*@jsdoc @@ -72,7 +72,7 @@ public slots: * // (0.739199, 0.280330, 0.612372, 0.000000), * // (10.000000, 11.000000, 12.000000, 1.000000)) */ - glm::mat4 createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const; + glm::mat<4,4,float,glm::packed_highp> createFromRotAndTrans(const glm::qua& rot, const glm::vec<3,float,glm::packed_highp>& trans) const; /*@jsdoc * Creates a matrix that represents a scale, rotation, and translation. @@ -92,7 +92,7 @@ public slots: * // (1.478398, 0.560660, 1.224745, 0.000000), * // (10.000000, 11.000000, 12.000000, 1.000000)) */ - glm::mat4 createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const; + glm::mat<4,4,float,glm::packed_highp> createFromScaleRotAndTrans(const glm::vec<3,float,glm::packed_highp>& scale, const glm::qua& rot, const glm::vec<3,float,glm::packed_highp>& trans) const; /*@jsdoc * Creates a matrix from columns of values. @@ -114,7 +114,7 @@ public slots: * // (1.478398, 0.560660, 1.224745, 0.000000), * // (10.000000, 11.000000, 12.000000, 1.000000)) */ - glm::mat4 createFromColumns(const glm::vec4& col0, const glm::vec4& col1, const glm::vec4& col2, const glm::vec4& col3) const; + glm::mat<4,4,float,glm::packed_highp> createFromColumns(const glm::vec<4,float,glm::packed_highp>& col0, const glm::vec<4,float,glm::packed_highp>& col1, const glm::vec<4,float,glm::packed_highp>& col2, const glm::vec<4,float,glm::packed_highp>& col3) const; /*@jsdoc * Creates a matrix from an array of values. @@ -135,7 +135,7 @@ public slots: * // (1.478398, 0.560660, 1.224745, 0.000000), * // (10.000000, 11.000000, 12.000000, 1.000000)) */ - glm::mat4 createFromArray(const QVector& floats) const; + glm::mat<4,4,float,glm::packed_highp> createFromArray(const QVector& floats) const; /*@jsdoc @@ -153,7 +153,7 @@ public slots: * print("Translation: " + JSON.stringify(trans)); * // Translation: {"x":10,"y":11,"z":12} */ - glm::vec3 extractTranslation(const glm::mat4& m) const; + glm::vec<3,float,glm::packed_highp> extractTranslation(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc * Extracts the rotation from a matrix. @@ -170,7 +170,7 @@ public slots: * print("Rotation: " + JSON.stringify(Quat.safeEulerAngles(rot))); * // Rotation: {"x":29.999998092651367,"y":45.00000762939453,"z":60.000003814697266} */ - glm::quat extractRotation(const glm::mat4& m) const; + glm::qua extractRotation(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc * Extracts the scale from a matrix. @@ -187,7 +187,7 @@ public slots: * print("Scale: " + JSON.stringify(scale)); * // Scale: {"x":1.9999998807907104,"y":1.9999998807907104,"z":1.9999998807907104} */ - glm::vec3 extractScale(const glm::mat4& m) const; + glm::vec<3,float,glm::packed_highp> extractScale(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc @@ -207,7 +207,7 @@ public slots: * print("Transformed point: " + JSON.stringify(transformedPoint)); * // Transformed point: { "x": 2.8284270763397217, "y": 12, "z": -2.384185791015625e-7 } */ - glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& point) const; + glm::vec<3,float,glm::packed_highp> transformPoint(const glm::mat<4,4,float,glm::packed_highp>& m, const glm::vec<3,float,glm::packed_highp>& point) const; /*@jsdoc * Transforms a vector into a new coordinate system: the vector is scaled and rotated. @@ -226,7 +226,7 @@ public slots: * print("Transformed vector: " + JSON.stringify(transformedVector)); * // Transformed vector: { "x": 2.8284270763397217, "y": 2, "z": -2.384185791015625e-7 } */ - glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& vector) const; + glm::vec<3,float,glm::packed_highp> transformVector(const glm::mat<4,4,float,glm::packed_highp>& m, const glm::vec<3,float,glm::packed_highp>& vector) const; /*@jsdoc @@ -247,7 +247,7 @@ public slots: * // (0.000000, 0.000000, 1.000000, 0.000000), * // (0.000000, 0.000000, 0.000001, 1.000000)) */ - glm::mat4 inverse(const glm::mat4& m) const; + glm::mat<4,4,float,glm::packed_highp> inverse(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc @@ -259,7 +259,7 @@ public slots: * @returns {Vec3} The negative z-axis rotated by orientation. */ // redundant, calls getForward which better describes the returned vector as a direction - glm::vec3 getFront(const glm::mat4& m) const { return getForward(m); } + glm::vec<3,float,glm::packed_highp> getFront(const glm::mat<4,4,float,glm::packed_highp>& m) const { return getForward(m); } /*@jsdoc * Gets the "forward" direction that the camera would face if its orientation was set to the rotation contained in a @@ -275,7 +275,7 @@ public slots: * print("Forward: " + JSON.stringify(forward)); * // Forward: {"x":0,"y":0,"z":-1} */ - glm::vec3 getForward(const glm::mat4& m) const; + glm::vec<3,float,glm::packed_highp> getForward(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc * Gets the "right" direction that the camera would have if its orientation was set to the rotation contained in a matrix. @@ -284,7 +284,7 @@ public slots: * @param {Mat4} m - The matrix. * @returns {Vec3} The x-axis rotated by the rotation in the matrix. */ - glm::vec3 getRight(const glm::mat4& m) const; + glm::vec<3,float,glm::packed_highp> getRight(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc * Gets the "up" direction that the camera would have if its orientation was set to the rotation contained in a matrix. The @@ -293,7 +293,7 @@ public slots: * @param {Mat4} m - The matrix. * @returns {Vec3} The y-axis rotated by the rotation in the matrix. */ - glm::vec3 getUp(const glm::mat4& m) const; + glm::vec<3,float,glm::packed_highp> getUp(const glm::mat<4,4,float,glm::packed_highp>& m) const; /*@jsdoc @@ -321,7 +321,7 @@ public slots: * // "r0c2": 1.4783978462219238, "r1c2": 0.5606603026390076, "r2c2": 1.2247447967529297, "r3c2": 0, * // "r0c3": 10, "r1c3": 11, "r2c3": 12, "r3c3": 1} */ - void print(const QString& label, const glm::mat4& m, bool transpose = false) const; + void print(const QString& label, const glm::mat<4,4,float,glm::packed_highp>& m, bool transpose = false) const; }; #endif // hifi_Mat4_h diff --git a/libraries/script-engine/src/MouseEvent.cpp b/libraries/script-engine/src/MouseEvent.cpp index 20f525e61b6..c3569a37a76 100644 --- a/libraries/script-engine/src/MouseEvent.cpp +++ b/libraries/script-engine/src/MouseEvent.cpp @@ -32,8 +32,8 @@ MouseEvent::MouseEvent() : MouseEvent::MouseEvent(const QMouseEvent& event) : - x(event.x()), - y(event.y()), + x(static_cast(event.position().x())), + y(static_cast(event.position().y())), isLeftButton(event.buttons().testFlag(Qt::LeftButton)), isRightButton(event.buttons().testFlag(Qt::RightButton)), isMiddleButton(event.buttons().testFlag(Qt::MiddleButton)), diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 75caf0e8af7..bfe7329e28c 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -21,6 +21,8 @@ #include +#include + #include #include @@ -60,7 +62,7 @@ /// Provides the Quat scripting interface class Quat : public QObject, protected Scriptable { Q_OBJECT - Q_PROPERTY(glm::quat IDENTITY READ IDENTITY CONSTANT) + Q_PROPERTY(glm::qua IDENTITY READ IDENTITY CONSTANT) public slots: @@ -77,7 +79,7 @@ public slots: * var handOrientation = Quat.multiply(MyAvatar.orientation, handPose.rotation); * } */ - glm::quat multiply(const glm::quat& q1, const glm::quat& q2); + glm::qua multiply(const glm::qua& q1, const glm::qua& q2); /*@jsdoc * Normalizes a quaternion. @@ -93,7 +95,7 @@ public slots: * // Use currentRotatation for something. * } */ - glm::quat normalize(const glm::quat& q); + glm::qua normalize(const glm::qua& q); /*@jsdoc * Calculates the conjugate of a quaternion. For a unit quaternion, its conjugate is the same as its @@ -109,7 +111,7 @@ public slots: * var identity = Quat.multiply(conjugate, quaternion); * Quat.print("identity", identity, true); // dvec3(0.000000, 0.000000, 0.000000) */ - glm::quat conjugate(const glm::quat& q); + glm::qua conjugate(const glm::qua& q); /*@jsdoc * Calculates a camera orientation given an eye position, point of interest, and "up" direction. The camera's negative @@ -125,7 +127,7 @@ public slots: * Camera.mode = "independent"; * Camera.orientation = Quat.lookAt(Camera.position, Vec3.ZERO, Vec3.UNIT_NEG_Y); */ - glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); + glm::qua lookAt(const glm::vec<3,float,glm::packed_highp>& eye, const glm::vec<3,float,glm::packed_highp>& center, const glm::vec<3,float,glm::packed_highp>& up); /*@jsdoc * Calculates a camera orientation given an eye position and point of interest. The camera's negative z-axis is the forward @@ -141,7 +143,7 @@ public slots: * Camera.mode = "independent"; * Camera.orientation = Quat.lookAtSimple(Camera.position, Vec3.ZERO); */ - glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); + glm::qua lookAtSimple(const glm::vec<3,float,glm::packed_highp>& eye, const glm::vec<3,float,glm::packed_highp>& center); /*@jsdoc * Calculates the shortest rotation from a first vector onto a second. @@ -158,7 +160,7 @@ public slots: * Entities.editEntity(entityID, properties); * entityVelocity = newVelocity; */ - glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); + glm::qua rotationBetween(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2); /*@jsdoc * Generates a quaternion from a {@link Vec3} of Euler angles in degrees. @@ -172,7 +174,7 @@ public slots: * eulerAngles.z = 0; * var newOrientation = Quat.fromVec3Degrees(eulerAngles); */ - glm::quat fromVec3Degrees(const glm::vec3& vec3); + glm::qua fromVec3Degrees(const glm::vec<3,float,glm::packed_highp>& vec3); /*@jsdoc * Generates a quaternion from a {@link Vec3} of Euler angles in radians. @@ -183,7 +185,7 @@ public slots: * @example Create a rotation of 180 degrees about the y axis. * var rotation = Quat.fromVec3Radians({ x: 0, y: Math.PI, z: 0 }); */ - glm::quat fromVec3Radians(const glm::vec3& vec3); + glm::qua fromVec3Radians(const glm::vec<3,float,glm::packed_highp>& vec3); /*@jsdoc * Generates a quaternion from pitch, yaw, and roll values in degrees. @@ -195,7 +197,7 @@ public slots: * @example Create a rotation of 180 degrees about the y axis. * var rotation = Quat.fromPitchYawRollDegrees(0, 180, 0 ); */ - glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); + glm::qua fromPitchYawRollDegrees(float pitch, float yaw, float roll); /*@jsdoc * Generates a quaternion from pitch, yaw, and roll values in radians. @@ -207,7 +209,7 @@ public slots: * @example Create a rotation of 180 degrees about the y axis. * var rotation = Quat.fromPitchYawRollRadians(0, Math.PI, 0); */ - glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); + glm::qua fromPitchYawRollRadians(float pitch, float yaw, float roll); /*@jsdoc * Calculates the inverse of a quaternion. For a unit quaternion, its inverse is the same as its @@ -223,7 +225,7 @@ public slots: * var identity = Quat.multiply(inverse, quaternion); * Quat.print("identity", identity, true); // dvec3(0.000000, 0.000000, 0.000000) */ - glm::quat inverse(const glm::quat& q); + glm::qua inverse(const glm::qua& q); /*@jsdoc * Gets the "front" direction that the camera would face if its orientation was set to the quaternion value. @@ -233,7 +235,7 @@ public slots: * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. */ - glm::vec3 getFront(const glm::quat& orientation) { return getForward(orientation); } + glm::vec<3,float,glm::packed_highp> getFront(const glm::qua& orientation) { return getForward(orientation); } /*@jsdoc * Gets the "forward" direction that the camera would face if its orientation was set to the quaternion value. @@ -246,7 +248,7 @@ public slots: * var forward = Quat.getForward(Quat.IDENTITY); * print(JSON.stringify(forward)); // {"x":0,"y":0,"z":-1} */ - glm::vec3 getForward(const glm::quat& orientation); + glm::vec<3,float,glm::packed_highp> getForward(const glm::qua& orientation); /*@jsdoc * Gets the "right" direction that the camera would have if its orientation was set to the quaternion value. @@ -255,7 +257,7 @@ public slots: * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The x-axis rotated by orientation. */ - glm::vec3 getRight(const glm::quat& orientation); + glm::vec<3,float,glm::packed_highp> getRight(const glm::qua& orientation); /*@jsdoc * Gets the "up" direction that the camera would have if its orientation was set to the quaternion value. @@ -264,7 +266,7 @@ public slots: * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The y-axis rotated by orientation. */ - glm::vec3 getUp(const glm::quat& orientation); + glm::vec<3,float,glm::packed_highp> getUp(const glm::qua& orientation); /*@jsdoc * Calculates the Euler angles for the quaternion, in degrees. (The "safe" in the name signifies that the angle results @@ -277,7 +279,7 @@ public slots: * var eulerAngles = Quat.safeEulerAngles(Camera.orientation); * print("Camera yaw: " + eulerAngles.y); */ - glm::vec3 safeEulerAngles(const glm::quat& orientation); + glm::vec<3,float,glm::packed_highp> safeEulerAngles(const glm::qua& orientation); /*@jsdoc * Generates a quaternion given an angle to rotate through and an axis to rotate about. @@ -290,7 +292,7 @@ public slots: * @example Calculate a rotation of 90 degrees about the direction your camera is looking. * var rotation = Quat.angleAxis(90, Quat.getForward(Camera.orientation)); */ - glm::quat angleAxis(float angle, const glm::vec3& v); + glm::qua angleAxis(float angle, const glm::vec<3,float,glm::packed_highp>& v); /*@jsdoc * Gets the rotation axis for a quaternion. @@ -304,7 +306,7 @@ public slots: * print("Forward: " + JSON.stringify(forward)); * print("Axis: " + JSON.stringify(axis)); // Same value as forward. */ - glm::vec3 axis(const glm::quat& orientation); + glm::vec<3,float,glm::packed_highp> axis(const glm::qua& orientation); /*@jsdoc * Gets the rotation angle for a quaternion. @@ -318,7 +320,7 @@ public slots: * var angle = Quat.angle(rotation); * print("Angle: " + angle * 180 / Math.PI); // 90 degrees. */ - float angle(const glm::quat& orientation); + float angle(const glm::qua& orientation); // spherical linear interpolation // alpha: 0.0 to 1.0? @@ -340,7 +342,7 @@ public slots: * } * var newRotation = Quat.mix(startRotation, endRotation, mixFactor); */ - glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); + glm::qua mix(const glm::qua& q1, const glm::qua& q2, float alpha); /*@jsdoc * Computes a spherical linear interpolation between two rotations, for rotations that are not very similar. @@ -353,7 +355,7 @@ public slots: * q1's value; 1.0 returns q2s's value. * @returns {Quat} A spherical linear interpolation between rotations q1 and q2. */ - glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); + glm::qua slerp(const glm::qua& q1, const glm::qua& q2, float alpha); /*@jsdoc * Computes a spherical quadrangle interpolation between two rotations along a path oriented toward two other rotations. @@ -368,7 +370,7 @@ public slots: * @returns {Quat} A spherical quadrangle interpolation between rotations q1 and q2 using control * points s1 and s2. */ - glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); + glm::qua squad(const glm::qua& q1, const glm::qua& q2, const glm::qua& s1, const glm::qua& s2, float h); /*@jsdoc * Calculates the dot product of two quaternions. The closer the quaternions are to each other the more non-zero the value @@ -389,7 +391,7 @@ public slots: * var equal = Math.abs(1 - Math.abs(dot)) < 0.000001; * print(equal); // true */ - float dot(const glm::quat& q1, const glm::quat& q2); + float dot(const glm::qua& q1, const glm::qua& q2); /*@jsdoc * Prints to the program log a text label followed by a quaternion's pitch, yaw, and roll Euler angles. @@ -407,7 +409,7 @@ public slots: * // Quaternion: {"x":0,"y":45.000003814697266,"z":0} * print("Quaternion: " + JSON.stringify(Quat.safeEulerAngles(quaternion))); */ - void print(const QString& label, const glm::quat& q, bool asDegrees = false); + void print(const QString& label, const glm::qua& q, bool asDegrees = false); /*@jsdoc * Tests whether two quaternions are equal. @@ -429,7 +431,7 @@ public slots: * var equal = Math.abs(1 - Math.abs(dot)) < 0.000001; * print(equal); // true */ - bool equal(const glm::quat& q1, const glm::quat& q2); + bool equal(const glm::qua& q1, const glm::qua& q2); /*@jsdoc * Cancels out the roll and pitch component of a quaternion so that its completely horizontal with a yaw pointing in the @@ -449,7 +451,7 @@ public slots: * Quat.print("", lookAt, true); // dvec3(0.000000, 22.245996, 0.000000) * */ - glm::quat cancelOutRollAndPitch(const glm::quat& q); + glm::qua cancelOutRollAndPitch(const glm::qua& q); /*@jsdoc * Cancels out the roll component of a quaternion so that its horizontal axis is level. @@ -467,10 +469,10 @@ public slots: * var lookAt = Quat.lookAtSimple(Vec3.ZERO, front); * Quat.print("", lookAt, true); // dvec3(-1.033004, 22.245996, -0.000000) */ - glm::quat cancelOutRoll(const glm::quat& q); + glm::qua cancelOutRoll(const glm::qua& q); private: - const glm::quat& IDENTITY() const { return Quaternions::IDENTITY; } + const glm::qua& IDENTITY() const { return Quaternions::IDENTITY; } }; diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 26a0f130a1e..cede6fb3782 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -17,6 +17,8 @@ #ifndef hifi_SceneScriptingInterface_h #define hifi_SceneScriptingInterface_h +#include + #include /*@jsdoc diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index b9e9c83ad1c..eee6f3474a6 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 41ffd9c346b..a8cb768eb56 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -18,6 +18,8 @@ #define hifi_ScriptCache_h #include +#include + #include using contentAvailableCallback = std::function; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index ed446269834..2e3e26b5b61 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -466,7 +466,7 @@ QStringList ScriptEngines::getRunningScripts() { } void ScriptEngines::stopAllScripts(bool restart) { - QtConcurrent::run([this, restart] { + QThreadPool::globalInstance()->start([this, restart] { QHash scriptManagersHashCopy; { @@ -557,13 +557,13 @@ ScriptManagerPointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool bool activateMainWindow, bool reload, bool quitWhenFinished) { if (thread() != QThread::currentThread()) { ScriptManagerPointer result { nullptr }; - BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptManagerPointer, result), - Q_ARG(QUrl, scriptFilename), - Q_ARG(bool, isUserLoaded), - Q_ARG(bool, loadScriptFromEditor), - Q_ARG(bool, activateMainWindow), - Q_ARG(bool, reload), - Q_ARG(bool, quitWhenFinished)); + BLOCKING_INVOKE_METHOD(this, "loadScript", Q_GENERIC_RETURN_ARG(ScriptManagerPointer, result), + Q_GENERIC_ARG(QUrl, scriptFilename), + Q_GENERIC_ARG(bool, isUserLoaded), + Q_GENERIC_ARG(bool, loadScriptFromEditor), + Q_GENERIC_ARG(bool, activateMainWindow), + Q_GENERIC_ARG(bool, reload), + Q_GENERIC_ARG(bool, quitWhenFinished)); return result; } QUrl scriptUrl; diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 97f798b804d..3da02d7d600 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -409,7 +409,7 @@ protected slots: ScriptManager::Context _context; QReadWriteLock _scriptManagersHashLock; - QMultiHash _scriptManagersHash; + QHash _scriptManagersHash; QSet _allKnownScriptManagers; QMutex _allScriptsMutex; ScriptsModel _scriptsModel; diff --git a/libraries/script-engine/src/ScriptException.h b/libraries/script-engine/src/ScriptException.h index f533d7a4f0e..77eb21d225a 100644 --- a/libraries/script-engine/src/ScriptException.h +++ b/libraries/script-engine/src/ScriptException.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "ScriptValue.h" @@ -146,20 +147,16 @@ class ScriptRuntimeException : public ScriptException { * * @return std::shared_ptr */ - virtual std::shared_ptr clone() const override { - return std::make_shared(*this); - } + virtual std::shared_ptr clone() const override { return std::make_shared(*this); } }; inline QDebug operator<<(QDebug debug, const ScriptException& e) { - debug << "Exception:" - << e.errorMessage - << (e.additionalInfo.isEmpty() ? QString("") : "[" + e.additionalInfo + "]") - << " at line " << e.errorLine << ", column " << e.errorColumn; + debug << "Exception:" << e.errorMessage << (e.additionalInfo.isEmpty() ? QString("") : "[" + e.additionalInfo + "]") + << " at line " << e.errorLine << ", column " << e.errorColumn; if (e.backtrace.length()) { debug << "Backtrace:"; - debug << e.backtrace; + debug << e.backtrace.join("\n"); } return debug; diff --git a/libraries/script-engine/src/ScriptManager.cpp b/libraries/script-engine/src/ScriptManager.cpp index c67b1d309db..4953741d2c5 100644 --- a/libraries/script-engine/src/ScriptManager.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -695,7 +696,7 @@ static ScriptValue scriptableResourceToScriptValue(ScriptEngine* engine, auto manager = engine->manager(); if (data && manager && !resource->isInScript()) { resource->setInScript(true); - QObject::connect(data.data(), &Resource::updateSize, manager, &ScriptManager::updateMemoryCost); + QObject::connect(data.get(), &Resource::updateSize, manager, &ScriptManager::updateMemoryCost); } auto object = engine->newQObject(const_cast(resource), ScriptEngine::ScriptOwnership); @@ -1639,7 +1640,7 @@ ScriptValue ScriptManager::instantiateModule(const ScriptValue& module, const QS // scoped vars for consistency with Node.js closure.setProperty("require", module.property("require")); closure.setProperty("__filename", modulePath, READONLY_HIDDEN_PROP_FLAGS); - closure.setProperty("__dirname", QString(modulePath).replace(QRegExp("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); + closure.setProperty("__dirname", QString(modulePath).replace(QRegularExpression("/[^/]*$"), ""), READONLY_HIDDEN_PROP_FLAGS); //_engine->scriptValueDebugDetails(module); result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath )); } @@ -2043,7 +2044,7 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID, c } QFuture ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) { - return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID, scriptURL); + return QtConcurrent::run(&ScriptManager::cloneEntityScriptDetails, this, entityID, scriptURL); } bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, EntityScriptDetails &details) const { @@ -2364,7 +2365,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c bool passList = false; // assume unsafe QString allowlistPrefix = "[ALLOWLIST ENTITY SCRIPTS]"; QList safeURLPrefixes = { "file:///", "atp:", "cache:" }; - safeURLPrefixes += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts); + safeURLPrefixes += qEnvironmentVariable("EXTRA_ALLOWLIST").trimmed().split(QRegularExpression("\\s*,\\s*"), Qt::SkipEmptyParts); // Entity Script Allowlist toggle check. Setting::Handle allowlistEnabled {"private/allowlistEnabled", false }; @@ -2375,7 +2376,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c // Pull SAFEURLS from the Interface.JSON settings. QVariant raw = Setting::Handle("private/settingsSafeURLS").get(); - QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegExp("\\s*[,\r\n]+\\s*"), Qt::SkipEmptyParts); + QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegularExpression("\\s*[,\r\n]+\\s*"), Qt::SkipEmptyParts); safeURLPrefixes += settingsSafeURLS; // END Pull SAFEURLS from the Interface.JSON settings. diff --git a/libraries/script-engine/src/ScriptManagerScriptingInterface.h b/libraries/script-engine/src/ScriptManagerScriptingInterface.h index 355e4920faa..a57255b27e8 100644 --- a/libraries/script-engine/src/ScriptManagerScriptingInterface.h +++ b/libraries/script-engine/src/ScriptManagerScriptingInterface.h @@ -338,7 +338,8 @@ class ScriptManagerScriptingInterface : public QObject { Q_INVOKABLE void clearInterval(QTimer* timer) { _manager->clearInterval(timer); } // Overloaded version is needed in case the timer has expired - Q_INVOKABLE void clearInterval(QVariantMap timer) { ; } + // QT6TODO: This errors out on Qt6. It seems to work without it, but I'm not sure if there isn't more of underlying bugs. + //Q_INVOKABLE void clearInterval(QVariantMap timer) { ; } /*@jsdoc * Stops a timeout timer set by {@link Script.setTimeout|setTimeout}. @@ -356,7 +357,8 @@ class ScriptManagerScriptingInterface : public QObject { Q_INVOKABLE void clearTimeout(QTimer* timer) { _manager->clearTimeout(timer); } // Overloaded version is needed in case the timer has expired - Q_INVOKABLE void clearTimeout(QVariantMap timer) { ; } + // QT6TODO: This errors out on Qt6. It seems to work without it, but I'm not sure if there isn't more of underlying bugs. + //Q_INVOKABLE void clearTimeout(QVariantMap timer) { ; } /*@jsdoc * Prints a message to the program log and emits {@link Script.printedMessage}. diff --git a/libraries/script-engine/src/ScriptMessage.cpp b/libraries/script-engine/src/ScriptMessage.cpp index 0b3ea1abc86..3650e02ad18 100644 --- a/libraries/script-engine/src/ScriptMessage.cpp +++ b/libraries/script-engine/src/ScriptMessage.cpp @@ -40,7 +40,7 @@ bool ScriptMessage::fromJson(const QJsonObject &object) { } _messageContent = object["message"].toString(); _lineNumber = object["lineNumber"].toInt(); - _fileName = object["fileName"].toInt(); + _fileName = object["fileName"].toString(); _entityID = QUuid::fromString(object["entityID"].toString()); _scriptType = static_cast(object["type"].toInt()); _severity = static_cast(object["severity"].toInt()); diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index 450e8797c50..f227b9d1d32 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -794,7 +794,7 @@ bool qColorFromScriptValue(const ScriptValue& object, QColor& color) { color.setRgb(object.toUInt32()); return true; } else if (object.isString()) { - color.setNamedColor(object.toString()); + color = QColor::fromString(object.toString()); return true; } else if (object.isArray()) { auto length = object.property("length").toInt32(); @@ -1097,7 +1097,8 @@ QVector qVectorEntityItemIDFromScriptValue(const ScriptValue& arra newVector.reserve(length); for (int i = 0; i < length; i++) { QString uuidAsString = array.property(i).toString(); - EntityItemID fromString(uuidAsString); + QUuid uuid(uuidAsString); + EntityItemID fromString(uuid); newVector << fromString; } return newVector; diff --git a/libraries/script-engine/src/ScriptValueUtils.h b/libraries/script-engine/src/ScriptValueUtils.h index 026f819d35a..20fc3eaa247 100644 --- a/libraries/script-engine/src/ScriptValueUtils.h +++ b/libraries/script-engine/src/ScriptValueUtils.h @@ -53,8 +53,8 @@ void registerMetaTypes(ScriptEngine* engine); * @property {number} r2c3 - Row 2, column 3 value. * @property {number} r3c3 - Row 3, column 3 value. */ -ScriptValue mat4toScriptValue(ScriptEngine* engine, const glm::mat4& mat4); -bool mat4FromScriptValue(const ScriptValue& object, glm::mat4& mat4); +ScriptValue mat4toScriptValue(ScriptEngine* engine, const glm::mat<4,4,float,glm::packed_highp>& mat4); +bool mat4FromScriptValue(const ScriptValue& object, glm::mat<4,4,float,glm::packed_highp>& mat4); /*@jsdoc * A 2-dimensional vector. @@ -148,9 +148,9 @@ bool u8vec3FromScriptValue(const ScriptValue& object, glm::u8vec3& vec3); * @property {number} z - Z-coordinate of the vector. * @property {number} w - W-coordinate of the vector. */ -ScriptValue vec4toScriptValue(ScriptEngine* engine, const glm::vec4& vec4); -ScriptValue vec4ColorToScriptValue(ScriptEngine* engine, const glm::vec4& vec4); -bool vec4FromScriptValue(const ScriptValue& object, glm::vec4& vec4); +ScriptValue vec4toScriptValue(ScriptEngine* engine, const glm::vec<4,float,glm::packed_highp>& vec4); +ScriptValue vec4ColorToScriptValue(ScriptEngine* engine, const glm::vec<4,float,glm::packed_highp>& vec4); +bool vec4FromScriptValue(const ScriptValue& object, glm::vec<4,float,glm::packed_highp>& vec4); // Quaternions ScriptValue quatToScriptValue(ScriptEngine* engine, const glm::quat& quat); diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 18adf103529..b1ef09377a7 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/libraries/script-engine/src/ScriptsModelFilter.cpp b/libraries/script-engine/src/ScriptsModelFilter.cpp index 1879e547c09..31ea329f68a 100644 --- a/libraries/script-engine/src/ScriptsModelFilter.cpp +++ b/libraries/script-engine/src/ScriptsModelFilter.cpp @@ -26,7 +26,7 @@ bool ScriptsModelFilter::lessThan(const QModelIndex& left, const QModelIndex& ri } bool ScriptsModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { - if (!filterRegExp().isEmpty()) { + if (!filterRegularExpression().pattern().isEmpty()) { ScriptsModel* scriptsModel = static_cast(sourceModel()); TreeNodeBase* node = scriptsModel->getFolderNodes( static_cast(scriptsModel->getTreeNodeFromIndex(sourceParent))).at(sourceRow); diff --git a/libraries/script-engine/src/TouchEvent.cpp b/libraries/script-engine/src/TouchEvent.cpp index f6b61a49f29..f21f2a6a86a 100644 --- a/libraries/script-engine/src/TouchEvent.cpp +++ b/libraries/script-engine/src/TouchEvent.cpp @@ -73,17 +73,17 @@ float angleBetweenPoints(const glm::vec2& a, const glm::vec2& b ) { void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { // convert the touch points into an average - const QList& tPoints = event.touchPoints(); + const QList& tPoints = event.points(); float touchAvgX = 0.0f; float touchAvgY = 0.0f; touchPoints = tPoints.count(); if (touchPoints > 1) { for (int i = 0; i < touchPoints; ++i) { - touchAvgX += (float)tPoints[i].pos().x(); - touchAvgY += (float)tPoints[i].pos().y(); + touchAvgX += (float)tPoints[i].position().x(); + touchAvgY += (float)tPoints[i].position().y(); // add it to our points vector - glm::vec2 thisPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); + glm::vec2 thisPoint(tPoints[i].position().x(), tPoints[i].position().y()); points << thisPoint; } touchAvgX /= (float)(touchPoints); @@ -91,8 +91,8 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { } else { // I'm not sure this should ever happen, why would Qt send us a touch event for only one point? // maybe this happens in the case of a multi-touch where all but the last finger is released? - touchAvgX = tPoints[0].pos().x(); - touchAvgY = tPoints[0].pos().y(); + touchAvgX = tPoints[0].position().x(); + touchAvgY = tPoints[0].position().y(); } x = touchAvgX; y = touchAvgY; @@ -102,7 +102,7 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { float maxRadius = 0.0f; glm::vec2 center(x,y); for (int i = 0; i < touchPoints; ++i) { - glm::vec2 touchPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); + glm::vec2 touchPoint(tPoints[i].position().x(), tPoints[i].position().y()); float thisRadius = glm::distance(center,touchPoint); if (thisRadius > maxRadius) { maxRadius = thisRadius; @@ -121,10 +121,10 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { } angle = totalAngle/(float)touchPoints; - isPressed = event.touchPointStates().testFlag(Qt::TouchPointPressed); - isMoved = event.touchPointStates().testFlag(Qt::TouchPointMoved); - isStationary = event.touchPointStates().testFlag(Qt::TouchPointStationary); - isReleased = event.touchPointStates().testFlag(Qt::TouchPointReleased); + isPressed = event.touchPointStates().testFlag(QEventPoint::State::Pressed); + isMoved = event.touchPointStates().testFlag(QEventPoint::State::Updated); + isStationary = event.touchPointStates().testFlag(QEventPoint::State::Stationary); + isReleased = event.touchPointStates().testFlag(QEventPoint::State::Released); // keyboard modifiers isShifted = event.modifiers().testFlag(Qt::ShiftModifier); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 026441f5fef..0ac79bda58b 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -18,6 +18,8 @@ #ifndef hifi_UsersScriptingInterface_h #define hifi_UsersScriptingInterface_h +#include + #include #include #include diff --git a/libraries/script-engine/src/VariantMapToScriptValue.cpp b/libraries/script-engine/src/VariantMapToScriptValue.cpp index b40f9fd735c..c27736b0984 100644 --- a/libraries/script-engine/src/VariantMapToScriptValue.cpp +++ b/libraries/script-engine/src/VariantMapToScriptValue.cpp @@ -18,26 +18,26 @@ #include "SharedLogging.h" ScriptValue variantToScriptValue(QVariant& qValue, ScriptEngine& scriptEngine) { - switch(qValue.type()) { - case QVariant::Bool: + switch(qValue.typeId()) { + case QMetaType::Bool: return scriptEngine.newValue(qValue.toBool()); break; - case QVariant::Int: + case QMetaType::Int: return scriptEngine.newValue(qValue.toInt()); break; - case QVariant::Double: + case QMetaType::Double: return scriptEngine.newValue(qValue.toDouble()); break; - case QVariant::String: - case QVariant::Url: + case QMetaType::QString: + case QMetaType::QUrl: return scriptEngine.newValue(qValue.toString()); break; - case QVariant::Map: { + case QMetaType::QVariantMap: { QVariantMap childMap = qValue.toMap(); return variantMapToScriptValue(childMap, scriptEngine); break; } - case QVariant::List: { + case QMetaType::QVariantList: { QVariantList childList = qValue.toList(); return variantListToScriptValue(childList, scriptEngine); break; diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index e63a7cd7ca7..df0eb79c7a8 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -113,7 +113,7 @@ public slots: * var reflected = Vec3.reflect(v, normal); * print(JSON.stringify(reflected)); // {"x":1,"y":-2,"z":3} */ - glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } + glm::vec<3,float,glm::packed_highp> reflect(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return glm::reflect(v1, v2); } /*@jsdoc * Calculates the cross product of two vectors. @@ -127,7 +127,7 @@ public slots: * var crossProduct = Vec3.cross(v1, v2); * print(JSON.stringify(crossProduct)); // {"x":0,"y":0,"z":1} */ - glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1, v2); } + glm::vec<3,float,glm::packed_highp> cross(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return glm::cross(v1, v2); } /*@jsdoc * Calculates the dot product of two vectors. @@ -141,7 +141,7 @@ public slots: * var dotProduct = Vec3.dot(v1, v2); * print(dotProduct); // 0 */ - float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } + float dot(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return glm::dot(v1, v2); } /*@jsdoc * Multiplies a vector by a scale factor. @@ -150,7 +150,7 @@ public slots: * @param {number} scale - The scale factor. * @returns {Vec3} The vector with each ordinate value multiplied by the scale. */ - glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } + glm::vec<3,float,glm::packed_highp> multiply(const glm::vec<3,float,glm::packed_highp>& v1, float f) { return v1 * f; } /*@jsdoc * Multiplies a vector by a scale factor. @@ -159,7 +159,7 @@ public slots: * @param {Vec3} v - The vector. * @returns {Vec3} The vector with each ordinate value multiplied by the scale. */ - glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } + glm::vec<3,float,glm::packed_highp> multiply(float f, const glm::vec<3,float,glm::packed_highp>& v1) { return v1 * f; } /*@jsdoc * Multiplies two vectors. @@ -174,7 +174,7 @@ public slots: * var multiplied = Vec3.multiplyVbyV(v1, v2); * print(JSON.stringify(multiplied)); // {"x":1,"y":4,"z":9} */ - glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; } + glm::vec<3,float,glm::packed_highp> multiplyVbyV(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return v1 * v2; } /*@jsdoc * Rotates a vector. @@ -188,7 +188,7 @@ public slots: * var result = Vec3.multiplyQbyV(q, v); * print(JSON.stringify(result)); // {"x":0,"y":1.000,"z":1.19e-7} */ - glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } + glm::vec<3,float,glm::packed_highp> multiplyQbyV(const glm::qua& q, const glm::vec<3,float,glm::packed_highp>& v) { return q * v; } /*@jsdoc * Calculates the sum of two vectors. @@ -197,7 +197,7 @@ public slots: * @param {Vec3} v2 - The second vector. * @returns {Vec3} The sum of the two vectors. */ - glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } + glm::vec<3,float,glm::packed_highp> sum(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return v1 + v2; } /*@jsdoc * Calculates one vector subtracted from another. @@ -206,7 +206,7 @@ public slots: * @param {Vec3} v2 - The second vector. * @returns {Vec3} The second vector subtracted from the first. */ - glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } + glm::vec<3,float,glm::packed_highp> subtract(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return v1 - v2; } /*@jsdoc * Calculates the length of a vector @@ -214,7 +214,7 @@ public slots: * @param {Vec3} v - The vector. * @returns {number} The length of the vector. */ - float length(const glm::vec3& v) { return glm::length(v); } + float length(const glm::vec<3,float,glm::packed_highp>& v) { return glm::length(v); } /*@jsdoc * Calculates the distance between two points. @@ -232,7 +232,7 @@ public slots: * distance = Vec3.distance(p1, p2); * print(distance); // 10 */ - float distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } + float distance(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return glm::distance(v1, v2); } /*@jsdoc * Calculates the angle of rotation from one vector onto another, with the sign depending on a reference vector. @@ -254,7 +254,7 @@ public slots: * print(Vec3.orientedAngle(v1, v2, { x: 1, y: 2, z: -1 })); // -45 * print(Vec3.orientedAngle(v1, v2, { x: 1, y: -2, z: -1 })); // 45 */ - float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); + float orientedAngle(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2, const glm::vec<3,float,glm::packed_highp>& v3); /*@jsdoc * Normalizes a vector so that its length is 1. @@ -267,7 +267,7 @@ public slots: * print(JSON.stringify(normalized)); // {"x":0.7071,"y":0.7071,"z":0} * print(Vec3.length(normalized)); // 1 */ - glm::vec3 normalize(const glm::vec3& v) { return glm::normalize(v); }; + glm::vec<3,float,glm::packed_highp> normalize(const glm::vec<3,float,glm::packed_highp>& v) { return glm::normalize(v); }; /*@jsdoc * Calculates a linear interpolation between two vectors. @@ -282,7 +282,7 @@ public slots: * var interpolated = Vec3.mix(v1, v2, 0.75); // 1/4 of v1 and 3/4 of v2. * print(JSON.stringify(interpolated)); // {"x":2.5,"y":7.5","z":0} */ - glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m) { return glm::mix(v1, v2, m); } + glm::vec<3,float,glm::packed_highp> mix(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2, float m) { return glm::mix(v1, v2, m); } /*@jsdoc * Prints the vector to the program log, as a text label followed by the vector value. @@ -294,7 +294,7 @@ public slots: * Vec3.print("Vector: ", v); // dvec3(1.000000, 2.000000, 3.000000) * print("Vector: " + JSON.stringify(v)); // {"x":1,"y":2,"z":3} */ - void print(const QString& label, const glm::vec3& v); + void print(const QString& label, const glm::vec<3,float,glm::packed_highp>& v); /*@jsdoc * Tests whether two vectors are equal. @@ -315,7 +315,7 @@ public slots: * equal = Vec3.equal(v1, v2); * print(equal); // false */ - bool equal(const glm::vec3& v1, const glm::vec3& v2) { return v1 == v2; } + bool equal(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2) { return v1 == v2; } /*@jsdoc * Tests whether two vectors are equal within a tolerance. @@ -336,7 +336,7 @@ public slots: * equal = Vec3.withinEpsilon(v1, v2, 0.001); * print(equal); // true */ - bool withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon); + bool withinEpsilon(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2, float epsilon); /*@jsdoc * Calculates polar coordinates (elevation, azimuth, radius) that transform the unit z-axis vector onto a point. @@ -352,7 +352,7 @@ public slots: * print("Radius: " + polar.z); // 7.5 */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation - glm::vec3 toPolar(const glm::vec3& v); + glm::vec<3,float,glm::packed_highp> toPolar(const glm::vec<3,float,glm::packed_highp>& v); /*@jsdoc * Calculates the coordinates of a point from polar coordinate transformation of the unit z-axis vector. @@ -366,7 +366,7 @@ public slots: * print(JSON.stringify(p)); // {"x":5,"y":2.5,"z":5} */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation - glm::vec3 fromPolar(const glm::vec3& polar); + glm::vec<3,float,glm::packed_highp> fromPolar(const glm::vec<3,float,glm::packed_highp>& polar); /*@jsdoc * Calculates the unit vector corresponding to polar coordinates elevation and azimuth transformation of the unit z-axis @@ -384,7 +384,7 @@ public slots: * print(JSON.stringify(p)); // {"x":5,"y":2.5,"z":5} */ // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation - glm::vec3 fromPolar(float elevation, float azimuth); + glm::vec<3,float,glm::packed_highp> fromPolar(float elevation, float azimuth); /*@jsdoc * Calculates the angle between two vectors. @@ -398,28 +398,28 @@ public slots: * var angle = Vec3.getAngle(v1, v2); * print(angle * 180 / Math.PI); // 90 */ - float getAngle(const glm::vec3& v1, const glm::vec3& v2); + float getAngle(const glm::vec<3,float,glm::packed_highp>& v1, const glm::vec<3,float,glm::packed_highp>& v2); private: - const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } - const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } - const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } - const glm::vec3& UNIT_NEG_X() { return Vectors::UNIT_NEG_X; } - const glm::vec3& UNIT_NEG_Y() { return Vectors::UNIT_NEG_Y; } - const glm::vec3& UNIT_NEG_Z() { return Vectors::UNIT_NEG_Z; } - const glm::vec3& UNIT_XY() { return Vectors::UNIT_XY; } - const glm::vec3& UNIT_XZ() { return Vectors::UNIT_XZ; } - const glm::vec3& UNIT_YZ() { return Vectors::UNIT_YZ; } - const glm::vec3& UNIT_XYZ() { return Vectors::UNIT_XYZ; } - const glm::vec3& FLOAT_MAX() { return Vectors::MAX; } - const glm::vec3& FLOAT_MIN() { return Vectors::MIN; } - const glm::vec3& ZERO() { return Vectors::ZERO; } - const glm::vec3& ONE() { return Vectors::ONE; } - const glm::vec3& TWO() { return Vectors::TWO; } - const glm::vec3& HALF() { return Vectors::HALF; } - const glm::vec3& RIGHT() { return Vectors::RIGHT; } - const glm::vec3& UP() { return Vectors::UP; } - const glm::vec3& FRONT() { return Vectors::FRONT; } + const glm::vec<3,float,glm::packed_highp>& UNIT_X() { return Vectors::UNIT_X; } + const glm::vec<3,float,glm::packed_highp>& UNIT_Y() { return Vectors::UNIT_Y; } + const glm::vec<3,float,glm::packed_highp>& UNIT_Z() { return Vectors::UNIT_Z; } + const glm::vec<3,float,glm::packed_highp>& UNIT_NEG_X() { return Vectors::UNIT_NEG_X; } + const glm::vec<3,float,glm::packed_highp>& UNIT_NEG_Y() { return Vectors::UNIT_NEG_Y; } + const glm::vec<3,float,glm::packed_highp>& UNIT_NEG_Z() { return Vectors::UNIT_NEG_Z; } + const glm::vec<3,float,glm::packed_highp>& UNIT_XY() { return Vectors::UNIT_XY; } + const glm::vec<3,float,glm::packed_highp>& UNIT_XZ() { return Vectors::UNIT_XZ; } + const glm::vec<3,float,glm::packed_highp>& UNIT_YZ() { return Vectors::UNIT_YZ; } + const glm::vec<3,float,glm::packed_highp>& UNIT_XYZ() { return Vectors::UNIT_XYZ; } + const glm::vec<3,float,glm::packed_highp>& FLOAT_MAX() { return Vectors::MAX; } + const glm::vec<3,float,glm::packed_highp>& FLOAT_MIN() { return Vectors::MIN; } + const glm::vec<3,float,glm::packed_highp>& ZERO() { return Vectors::ZERO; } + const glm::vec<3,float,glm::packed_highp>& ONE() { return Vectors::ONE; } + const glm::vec<3,float,glm::packed_highp>& TWO() { return Vectors::TWO; } + const glm::vec<3,float,glm::packed_highp>& HALF() { return Vectors::HALF; } + const glm::vec<3,float,glm::packed_highp>& RIGHT() { return Vectors::RIGHT; } + const glm::vec<3,float,glm::packed_highp>& UP() { return Vectors::UP; } + const glm::vec<3,float,glm::packed_highp>& FRONT() { return Vectors::FRONT; } public: virtual ~Vec3(); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index dada3029510..5e135d9d358 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -51,7 +51,7 @@ void WebSocketClass::initialize() { connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); connect(_webSocket, &QWebSocket::binaryMessageReceived, this, &WebSocketClass::handleOnBinaryMessage); connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); - connect(_webSocket, static_cast(&QWebSocket::error), this, + connect(_webSocket, static_cast(&QWebSocket::errorOccurred), this, &WebSocketClass::handleOnError); _binaryType = QStringLiteral("arraybuffer"); } diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 46e35d0c961..6a11b05392e 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -263,14 +263,14 @@ void XMLHttpRequestClass::abortRequest() { void XMLHttpRequestClass::connectToReply(QNetworkReply* reply) { connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); connect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); } void XMLHttpRequestClass::disconnectFromReply(QNetworkReply* reply) { disconnect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); - disconnect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + disconnect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); disconnect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); } diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index 7886d6865d3..9a6123b49e4 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -757,9 +757,9 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f "sourceCode:" << sourceCode << " fileName:" << fileName; #endif BLOCKING_INVOKE_METHOD(this, "evaluate", - Q_RETURN_ARG(ScriptValue, result), - Q_ARG(const QString&, sourceCode), - Q_ARG(const QString&, fileName)); + Q_GENERIC_RETURN_ARG(ScriptValue, result), + Q_GENERIC_ARG(const QString&, sourceCode), + Q_GENERIC_ARG(const QString&, fileName)); return result; }*/ // Compile and check syntax @@ -998,8 +998,8 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro "sourceCode:" << sourceCode << " fileName:" << fileName; #endif BLOCKING_INVOKE_METHOD(this, "evaluate", - Q_RETURN_ARG(ScriptValue, result), - Q_ARG(const ScriptProgramPointer&, program)); + Q_GENERIC_RETURN_ARG(ScriptValue, result), + Q_GENERIC_ARG(const ScriptProgramPointer&, program)); return result; } _evaluatingCounter++; @@ -1381,9 +1381,9 @@ ScriptValue ScriptEngineV8::create(int type, const void* ptr) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); v8::Context::Scope contextScope(getContext()); - QVariant variant(type, ptr); + QVariant variant(QMetaType(type), ptr); V8ScriptValue scriptValue = castVariantToValue(variant); - return ScriptValue(new ScriptValueV8Wrapper(this, std::move(scriptValue))); + return {new ScriptValueV8Wrapper(this, std::move(scriptValue))}; } QVariant ScriptEngineV8::convert(const ScriptValue& value, int typeId) { @@ -1392,21 +1392,21 @@ QVariant ScriptEngineV8::convert(const ScriptValue& value, int typeId) { v8::Context::Scope contextScope(getContext()); ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value); if (unwrapped == nullptr) { - return QVariant(); + return {}; } QVariant var; if (!castValueToVariant(unwrapped->toV8Value(), var, typeId)) { - return QVariant(); + return {}; } int destType = var.userType(); if (destType != typeId) { - var.convert(typeId); // if conversion fails then var is set to QVariant() + var.convert(QMetaType(typeId)); // if conversion fails then var is set to QVariant() } return var; - return QVariant(); + return {}; } void ScriptEngineV8::processEvents() { diff --git a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp index 9dfce6f6e9c..ff0ee990859 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8_cast.cpp @@ -300,7 +300,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de if (obj) { for (const QMetaObject* metaObject = obj->metaObject(); metaObject; metaObject = metaObject->superClass()) { QByteArray typeName = QByteArray(metaObject->className()) + "*"; - int typeId = QMetaType::type(typeName.constData()); + int typeId = QMetaType::fromName(typeName).id(); if (typeId != QMetaType::UnknownType) { destTypeId = typeId; break; @@ -382,7 +382,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de } } errorMessage = QString() + "Conversion failure: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked())) - + "to variant. Destination type: " + QMetaType::typeName(destTypeId) +" details: "+ scriptValueDebugDetailsV8(v8Val); + + "to variant. Destination type: " + QMetaType(destTypeId).name() +" details: "+ scriptValueDebugDetailsV8(v8Val); qCDebug(scriptengine_v8) << errorMessage; // V8TODO: this doesn't seem to be necessary anymore but I'm keeping it until all the API is tested @@ -501,12 +501,13 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de } } errorMessage = QString() + "Conversion to variant failed: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked())) - + " Destination type: " + QMetaType::typeName(destTypeId) + " Value details: " + scriptValueDebugDetailsV8(v8Val); + + " Destination type: " + QMetaType(destTypeId).name() + " Value details: " + scriptValueDebugDetailsV8(v8Val); qCDebug(scriptengine_v8) << errorMessage; return false; default: // check to see if this is a pointer to a QObject-derived object - if (QMetaType::typeFlags(destTypeId) & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) { + const auto destMetaType = QMetaType(destTypeId); + if (destMetaType.flags() & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) { /* Do we really want to permit regular passing of nullptr to native functions? if (!val.isValid() || val.isUndefined() || val.isNull()) { dest = QVariant::fromValue(nullptr); @@ -514,7 +515,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de }*/ QObject* obj = ScriptObjectV8Proxy::unwrap(v8Val); if (!obj) return false; - const QMetaObject* destMeta = QMetaType::metaObjectForType(destTypeId); + const QMetaObject* destMeta = destMetaType.metaObject(); Q_ASSERT(destMeta); obj = destMeta->cast(obj); if (!obj) return false; @@ -540,7 +541,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de // last chance, just convert it to a variant (impossible on V8) // V8TODO errorMessage = QString() + "Conversion failure: " + QString(*v8::String::Utf8Value(_v8Isolate, val->ToDetailString(getConstContext()).ToLocalChecked())) - + "to variant. Destination type: " + QMetaType::typeName(destTypeId); + + "to variant. Destination type: " + destMetaType.name(); qCDebug(scriptengine_v8) << errorMessage; if(destTypeId == QMetaType::QVariant) { Q_ASSERT(false); @@ -550,7 +551,7 @@ bool ScriptEngineV8::castValueToVariant(const V8ScriptValue& v8Val, QVariant& de } } - return destTypeId == QMetaType::UnknownType || dest.userType() == destTypeId || dest.convert(destTypeId); + return destTypeId == QMetaType::UnknownType || dest.userType() == destTypeId || dest.convert(QMetaType(destTypeId)); } bool ScriptEngineV8::convertJSArrayToVariant(v8::Local array, QVariant &dest) { @@ -562,7 +563,7 @@ bool ScriptEngineV8::convertJSArrayToVariant(v8::Local array, QVarian for (int i = 0; i < length; i++) { v8::Local v8Property; if (!array->Get(context, i).ToLocal(&v8Property)) { - qCDebug(scriptengine_v8) << "ScriptEngineV8::convertJSArrayToVariant could not get property: " + QString(i); + qCDebug(scriptengine_v8) << "ScriptEngineV8::convertJSArrayToVariant could not get property: " + QString::number(i); continue; } QVariant property; @@ -570,7 +571,7 @@ bool ScriptEngineV8::convertJSArrayToVariant(v8::Local array, QVarian if (castValueToVariant(V8ScriptValue(this, v8Property), property, QMetaType::UnknownType)) { properties.append(property); } else { - qCDebug(scriptengine_v8) << "ScriptEngineV8::convertJSArrayToVariant could cast property to variant: " + QString(i); + qCDebug(scriptengine_v8) << "ScriptEngineV8::convertJSArrayToVariant could cast property to variant: " + QString::number(i); ; } } @@ -654,6 +655,9 @@ QString ScriptEngineV8::valueType(const V8ScriptValue& v8Val) { return "undefined"; } +// QT6TODO: where does this belong? +Q_DECLARE_METATYPE(Qt::TimerType); + V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { Q_ASSERT(_v8Isolate->IsCurrent()); v8::HandleScope handleScope(_v8Isolate); @@ -731,12 +735,18 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { default: // check to see if this is a pointer to a QObject-derived object // WeakPointerToQObject and SharedPointerToQObject were causing trouble here because some values are handled by custom prototypes instead - if (QMetaType::typeFlags(valTypeId) & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) { + if (QMetaType(valTypeId).flags() & (QMetaType::PointerToQObject | QMetaType::TrackingPointerToQObject)) { QObject* obj = val.value(); if (obj == nullptr) return V8ScriptValue(this, v8::Null(_v8Isolate)); //V8TODO: what should be the ownership in this case? return ScriptObjectV8Proxy::newQObject(this, obj); } + + // enums need special treatment, Qt::TimerType fails without this + if (QMetaType(valTypeId).flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) { + return V8ScriptValue(this, v8::Integer::New(_v8Isolate, val.toInt())); + } + // have we set a prototyped variant? { _customTypeProtect.lockForRead(); @@ -748,7 +758,7 @@ V8ScriptValue ScriptEngineV8::castVariantToValue(const QVariant& val) { } // just do a generic variant //V8TODO - qCDebug(scriptengine_v8) << "ScriptEngineV8::castVariantToValue failed for " << QMetaType::typeName(valTypeId); + qCDebug(scriptengine_v8) << "ScriptEngineV8::castVariantToValue failed for " << QMetaType(valTypeId).name(); logBacktrace("ScriptEngineV8::castVariantToValue failed"); //Q_ASSERT(false); return V8ScriptValue(this, v8::Undefined(_v8Isolate)); diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp index 7c1de57f583..039e0d95d51 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.cpp @@ -23,6 +23,7 @@ #include "ScriptContextV8Wrapper.h" #include "ScriptValueV8Wrapper.h" #include "ScriptEngineLoggingV8.h" +#include "shared/QtHelpers.h" Q_DECLARE_METATYPE(ScriptValue) @@ -297,10 +298,10 @@ void ScriptObjectV8Proxy::investigate() { } PropertyDef& propDef = _props.insert(idx, PropertyDef(prop.name(), idx)).value(); - _propNameMap.insert(prop.name(), &propDef); propDef.flags = ScriptValue::Undeletable | ScriptValue::PropertyGetter | ScriptValue::PropertySetter | ScriptValue::QObjectMember; if (prop.isConstant()) propDef.flags |= ScriptValue::ReadOnly; + _propNameMap.insert(prop.name(), propDef); } // discover methods @@ -348,7 +349,7 @@ void ScriptObjectV8Proxy::investigate() { SignalDef& signalDef = _signals.insert(idx, SignalDef(szName, idx)).value(); signalDef.name = szName; signalDef.signal = method; - _signalNameMap.insert(szName, &signalDef); + _signalNameMap.insert(szName, signalDef); methodNames.insert(szName, idx); } else { int originalMethodId = nameLookup.value(); @@ -375,7 +376,7 @@ void ScriptObjectV8Proxy::investigate() { methodDef.name = szName; methodDef.numMaxParams = parameterCount; methodDef.methods.append(method); - _methodNameMap.insert(szName, &methodDef); + _methodNameMap.insert(szName, methodDef); methodNames.insert(szName, idx); } else { int originalMethodId = nameLookup.value(); @@ -439,16 +440,16 @@ ScriptObjectV8Proxy::QueryFlags ScriptObjectV8Proxy::queryProperty(const V8Scrip // check for methods MethodNameMap::const_iterator method = _methodNameMap.find(nameStr); if (method != _methodNameMap.cend()) { - const MethodDef* methodDef = method.value(); - *id = methodDef->_id | METHOD_TYPE; + const MethodDef &methodDef = method.value(); + *id = methodDef._id | METHOD_TYPE; return flags & (HandlesReadAccess | HandlesWriteAccess); } // check for properties PropertyNameMap::const_iterator prop = _propNameMap.find(nameStr); if (prop != _propNameMap.cend()) { - const PropertyDef* propDef = prop.value(); - *id = propDef->_id | PROPERTY_TYPE; + const PropertyDef &propDef = prop.value(); + *id = propDef._id | PROPERTY_TYPE; return flags & (HandlesReadAccess | HandlesWriteAccess); } @@ -1022,6 +1023,20 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume for (int i = 0; i < num_metas; i++) { const QMetaMethod& meta = _metas[i]; + qVarArgLists[i].reserve(_numMaxParams); + + // This check is needed for catching issues caused by a bug in Qt6 that causes metamethod invocations to fail when typedefs are used in header files. +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + for (int parameterIndex = 0; parameterIndex < meta.parameterCount(); parameterIndex++) { + if (std::string(meta.parameterTypeName(parameterIndex)) != std::string(meta.parameterMetaType(parameterIndex).name())) { + qCritical() << "ScriptMethodV8Proxy::call: " << fullName() << " parameter " << parameterIndex + << " has a broken type " << meta.parameterTypeName(parameterIndex) + << " Correct type is " << meta.parameterMetaType(parameterIndex).name(); + Q_ASSERT(false); + } + } +#endif + int methodNumArgs = meta.parameterCount(); if (methodNumArgs != numArgs) { continue; @@ -1041,28 +1056,27 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume v8::Local argVal = arguments[arg]; if (methodArgTypeId == scriptValueTypeId) { qScriptArgLists[i].append(ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, argVal)))); - qGenArgsVectors[i][arg] = Q_ARG(ScriptValue, qScriptArgLists[i].back()); + qGenArgsVectors[i][arg] = Q_GENERIC_ARG(ScriptValue, qScriptArgLists[i].back()); } else if (methodArgTypeId == QMetaType::QVariant) { - QVariant varArgVal; - if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), varArgVal, methodArgTypeId)) { + qVarArgLists[i].emplace_back(); + if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), qVarArgLists[i].back(), methodArgTypeId)) { conversionFailures++; + qVarArgLists[i].pop_back(); } else { - qVarArgLists[i].append(varArgVal); - qGenArgsVectors[i][arg] = Q_ARG(QVariant, qVarArgLists[i].back()); + qGenArgsVectors[i][arg] = Q_GENERIC_ARG(QVariant, qVarArgLists[i].back()); } } else { - QVariant varArgVal; - if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), varArgVal, methodArgTypeId)) { + qVarArgLists[i].emplace_back(); + if (!_engine->castValueToVariant(V8ScriptValue(_engine, argVal), qVarArgLists[i].back(), methodArgTypeId)) { conversionFailures++; } else { - qVarArgLists[i].append(varArgVal); const QVariant& converted = qVarArgLists[i].back(); conversionPenaltyScore += _engine->computeCastPenalty(V8ScriptValue(_engine, argVal), methodArgTypeId); // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant // A const_cast is needed because calling data() would detach the QVariant. qGenArgsVectors[i][arg] = - QGenericArgument(QMetaType::typeName(converted.userType()), const_cast(converted.constData())); + QGenericArgument(QMetaType(converted.userType()).name(), const_cast(converted.constData())); } } } @@ -1114,7 +1128,7 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume return; } else if (returnTypeId == scriptValueTypeId) { ScriptValue result; - bool success = meta.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(ScriptValue, result), qGenArgs[0], + bool success = meta.invoke(qobject, Qt::DirectConnection, Q_GENERIC_RETURN_ARG(ScriptValue, result), qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3], qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]); if (!success) { @@ -1127,7 +1141,7 @@ void ScriptMethodV8Proxy::call(const v8::FunctionCallbackInfo& argume } else { // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant const char* typeName = meta.typeName(); - QVariant qRetVal(returnTypeId, static_cast(NULL)); + QVariant qRetVal(QMetaType(returnTypeId), static_cast(NULL)); QGenericReturnArgument sRetVal(typeName, const_cast(qRetVal.constData())); bool success = @@ -1265,7 +1279,7 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu for (int arg = 0; arg < numArgs; ++arg) { int methodArgTypeId = _meta.parameterType(arg); Q_ASSERT(methodArgTypeId != QMetaType::UnknownType); - QVariant argValue(methodArgTypeId, arguments[arg + 1]); + QVariant argValue(QMetaType(methodArgTypeId), arguments[arg + 1]); args[arg] = _engine->castVariantToValue(argValue).get(); } for (ConnectionList::iterator iter = connections.begin(); iter != connections.end(); ++iter) { diff --git a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h index 14b8f1900eb..3d7584aae36 100644 --- a/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h +++ b/libraries/script-engine/src/v8/ScriptObjectV8Proxy.h @@ -69,9 +69,9 @@ class ScriptObjectV8Proxy final { using MethodDefMap = QHash; using SignalDefMap = QHash; using InstanceMap = QHash >; - using PropertyNameMap = QHash; - using MethodNameMap = QHash; - using SignalNameMap = QHash; + using PropertyNameMap = QHash; + using MethodNameMap = QHash; + using SignalNameMap = QHash; static constexpr uint PROPERTY_TYPE = 0x1000; static constexpr uint METHOD_TYPE = 0x2000; diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index 0bc4814b715..01ab81cdcc5 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -453,7 +453,7 @@ void ScriptValueV8Wrapper::setProperty(quint32 arrayIndex, const ScriptValue& va qCDebug(scriptengine_v8) << "Failed to set property"; } } else { - qCDebug(scriptengine_v8) << "Failed to set property: " + QString(arrayIndex) + " - parent is not an object"; + qCDebug(scriptengine_v8) << "Failed to set property: " + QString::number(arrayIndex) + " - parent is not an object"; } //V8TODO: what about flags? //_value.setProperty(arrayIndex, unwrapped, (V8ScriptValue::PropertyFlags)(int)flags); diff --git a/libraries/shared/src/ApplicationVersion.cpp b/libraries/shared/src/ApplicationVersion.cpp index 5c2d5ad11ce..c6be7532b76 100644 --- a/libraries/shared/src/ApplicationVersion.cpp +++ b/libraries/shared/src/ApplicationVersion.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include ApplicationVersion::ApplicationVersion(const QString& versionString) : diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h index c5ba2905144..88681101135 100644 --- a/libraries/shared/src/DebugDraw.h +++ b/libraries/shared/src/DebugDraw.h @@ -10,6 +10,10 @@ #ifndef hifi_DebugDraw_h #define hifi_DebugDraw_h +#include + +#include "RegisteredMetaTypes.h" + #include #include #include @@ -18,7 +22,6 @@ #include #include -#include #include /*@jsdoc @@ -58,7 +61,7 @@ class DebugDraw : public QObject { * DebugDraw.drawRay(start, end, color); * }); */ - Q_INVOKABLE void drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color); + Q_INVOKABLE void drawRay(const glm::vec<3,float,glm::packed_highp>& start, const glm::vec<3,float,glm::packed_highp>& end, const glm::vec<4,float,glm::packed_highp>& color); /*@jsdoc * Draws lines in world space, visible for a single frame. To make the lines visually persist, you need to repeatedly draw @@ -83,8 +86,8 @@ class DebugDraw : public QObject { * DebugDraw.drawRays(lines, color, translation, rotation); * }); */ - Q_INVOKABLE void drawRays(const std::vector>& lines, const glm::vec4& color, - const glm::vec3& translation = glm::vec3(0.0f, 0.0f, 0.0f), const glm::quat& rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + Q_INVOKABLE void drawRays(const std::vector, glm::vec<3,float,glm::packed_highp>>>& lines, const glm::vec<4,float,glm::packed_highp>& color, + const glm::vec<3,float,glm::packed_highp>& translation = glm::vec<3,float,glm::packed_highp>(0.0f, 0.0f, 0.0f), const glm::qua& rotation = glm::qua(1.0f, 0.0f, 0.0f, 0.0f)); /*@jsdoc * Adds or updates a debug marker in world coordinates. This marker is drawn every frame until it is removed using @@ -109,8 +112,8 @@ class DebugDraw : public QObject { * DebugDraw.removeMarker(MARKER_NAME); * }, 5000); */ - Q_INVOKABLE void addMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, - const glm::vec4& color, float size = 1.0f); + Q_INVOKABLE void addMarker(const QString& key, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& position, + const glm::vec<4,float,glm::packed_highp>& color, float size = 1.0f); /*@jsdoc * Removes a debug marker that was added in world coordinates. @@ -142,8 +145,8 @@ class DebugDraw : public QObject { * DebugDraw.removeMyAvatarMarker(MARKER_NAME); * }, 5000); */ - Q_INVOKABLE void addMyAvatarMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, - const glm::vec4& color, float size = 1.0f); + Q_INVOKABLE void addMyAvatarMarker(const QString& key, const glm::qua& rotation, const glm::vec<3,float,glm::packed_highp>& position, + const glm::vec<4,float,glm::packed_highp>& color, float size = 1.0f); /*@jsdoc * Removes a debug marker that was added in avatar coordinates. @@ -165,8 +168,8 @@ class DebugDraw : public QObject { MarkerMap getMyAvatarMarkerMap() const; void updateMyAvatarPos(const glm::vec3& pos) { _myAvatarPos = pos; } const glm::vec3& getMyAvatarPos() const { return _myAvatarPos; } - void updateMyAvatarRot(const glm::quat& rot) { _myAvatarRot = rot; } - const glm::quat& getMyAvatarRot() const { return _myAvatarRot; } + void updateMyAvatarRot(const glm::qua& rot) { _myAvatarRot = rot; } + const glm::qua& getMyAvatarRot() const { return _myAvatarRot; } Rays getRays() const; void clearRays(); diff --git a/libraries/shared/src/Grab.cpp b/libraries/shared/src/Grab.cpp index 195bc61f66c..118794ff70b 100644 --- a/libraries/shared/src/Grab.cpp +++ b/libraries/shared/src/Grab.cpp @@ -11,6 +11,8 @@ #include "Grab.h" +#include + QByteArray Grab::toByteArray() { QByteArray ba; QDataStream dataStream(&ba, QIODevice::WriteOnly); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 71748ffd10e..c9030b69d5b 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -20,19 +20,20 @@ #include #include #include +#include #include "PathUtils.h" #include "SharedLogging.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { - QMultiMap mergedMap; + QVariantMap mergedMap; // Add anything in the CL parameter list to the variant map. // Take anything with a dash in it as a key, and the values after it as the value. const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)"; - QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING); + QRegularExpression dashedKeyRegex(DASHED_KEY_REGEX_STRING); int keyIndex = argumentList.indexOf(dashedKeyRegex); int nextKeyIndex = 0; @@ -43,7 +44,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL while (keyIndex != -1) { if (argumentList[keyIndex] != CONFIG_FILE_OPTION) { // we have a key - look forward to see how many values associate to it - QString key = dashedKeyRegex.cap(2); + QString key = dashedKeyRegex.namedCaptureGroups()[2]; nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); @@ -115,7 +116,7 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa if (existingMap.contains(key)) { // if this is just a regular value, we're done - we don't ovveride - if (newMap[key].canConvert(QMetaType::QVariantMap) && existingMap[key].canConvert(QMetaType::QVariantMap)) { + if (newMap[key].canConvert(QMetaType(QMetaType::QVariantMap)) && existingMap[key].canConvert(QMetaType(QMetaType::QVariantMap))) { // there's a variant map below and the existing map has one too, so we need to keep recursing addMissingValuesToExistingMap(*static_cast(existingMap[key].data()), newMap[key].toMap()); } @@ -134,7 +135,7 @@ QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool if (dotIndex == -1) { return &variantMap[firstKey]; } - if (!variantMap[firstKey].canConvert(QMetaType::QVariantMap)) { + if (!variantMap[firstKey].canConvert(QMetaType(QMetaType::QVariantMap))) { variantMap[firstKey] = QVariantMap(); } return valueForKeyPath(*static_cast(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1), diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h index e64f8e29c35..60c85bd1009 100644 --- a/libraries/shared/src/LogHandler.h +++ b/libraries/shared/src/LogHandler.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index be605334068..283fabe3fd2 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -192,7 +192,7 @@ bool PathUtils::deleteMyTemporaryDir(QString dirName) { QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?\\d+)\\-(?\\d+)$" }; auto match = re.match(dirName); - auto pid = match.capturedRef("pid").toLongLong(); + auto pid = match.capturedView("pid").toLongLong(); if (match.hasMatch() && rootTempDir.exists(dirName) && pid == qApp->applicationPid()) { auto absoluteDirPath = QDir(rootTempDir.absoluteFilePath(dirName)); @@ -227,8 +227,7 @@ int PathUtils::removeTemporaryApplicationDirs(QString appName) { auto match = re.match(dirName); if (match.hasMatch()) { - auto pid = match.capturedRef("pid").toLongLong(); - auto timestamp = match.capturedRef("timestamp"); + auto pid = match.capturedView("pid").toLongLong(); if (!processIsRunning(pid)) { qDebug() << " Removing old temporary directory: " << dir.absoluteFilePath(); absoluteDirPath.removeRecursively(); @@ -246,7 +245,7 @@ QString fileNameWithoutExtension(const QString& fileName, const QVector QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { if (fileNameLowered.endsWith(possibleExtension.toLower())) { - return fileName.left(fileName.count() - possibleExtension.count() - 1); + return fileName.left(fileName.size() - possibleExtension.size() - 1); } } return fileName; diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index b3b742e1ba3..99b57224bbf 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -20,6 +20,9 @@ #include #include +#include +#include + #include "AACube.h" #include "ShapeInfo.h" #include "SharedUtil.h" @@ -44,6 +47,7 @@ Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(AACube) Q_DECLARE_METATYPE(std::function); Q_DECLARE_METATYPE(std::function); +Q_DECLARE_METATYPE(QTimer*); // Mat4 /*@jsdoc diff --git a/libraries/shared/src/RunningMarker.cpp b/libraries/shared/src/RunningMarker.cpp index cb7b39320c4..09e16a40253 100644 --- a/libraries/shared/src/RunningMarker.cpp +++ b/libraries/shared/src/RunningMarker.cpp @@ -47,5 +47,5 @@ void RunningMarker::deleteRunningMarkerFile() { } QString RunningMarker::getFilePath() const { - return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name; + return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + _name; } diff --git a/libraries/shared/src/Sampler.cpp b/libraries/shared/src/Sampler.cpp index f106a218731..4e0a45237aa 100644 --- a/libraries/shared/src/Sampler.cpp +++ b/libraries/shared/src/Sampler.cpp @@ -126,45 +126,45 @@ QDebug& operator<<(QDebug& dbg, const Sampler& s) { QString result = "[ "; result += "borderColor: ("; - result += s.getBorderColor().r; + result += QString::number(s.getBorderColor().r); result += ", "; - result += s.getBorderColor().g; + result += QString::number(s.getBorderColor().g); result += ", "; - result += s.getBorderColor().b; + result += QString::number(s.getBorderColor().b); result += ", "; - result += s.getBorderColor().a; + result += QString::number(s.getBorderColor().a); result += "), "; result += "maxAnistropy: "; - result += s.getMaxAnisotropy(); + result += QString::number(s.getMaxAnisotropy()); result += ", "; result += "filter: "; - result += s.getFilter(); + result += QString::number(s.getFilter()); result += ", "; result += "comparisonFunction: "; - result += (uint8_t)s.getComparisonFunction(); + result += QString::number((uint8_t)s.getComparisonFunction()); result += ", "; result += "wrap: ("; - result += s.getWrapModeU(); + result += QString::number(s.getWrapModeU()); result += ", "; - result += s.getWrapModeV(); + result += QString::number(s.getWrapModeV()); result += ", "; - result += s.getWrapModeW(); + result += QString::number(s.getWrapModeW()); result += "), "; result += "mipOffset: "; - result += s.getMipOffset(); + result += QString::number(s.getMipOffset()); result += ", "; result += "minMip: "; - result += s.getMinMip(); + result += QString::number(s.getMinMip()); result += ", "; result += "maxMip: "; - result += s.getMaxMip(); + result += QString::number(s.getMaxMip()); result += ", "; result += "]"; diff --git a/libraries/shared/src/SettingHelpers.cpp b/libraries/shared/src/SettingHelpers.cpp index b4caa2f7bf4..8882aa630cd 100644 --- a/libraries/shared/src/SettingHelpers.cpp +++ b/libraries/shared/src/SettingHelpers.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "SharedLogging.h" @@ -64,10 +65,10 @@ void loadOldINIFile(QSettings& settings) { if (!iniSettings.allKeys().isEmpty()) { qCDebug(shared) << "No data in json settings file, trying to load old ini settings file."; - for (auto key : iniSettings.allKeys()) { + for (const auto &key : iniSettings.allKeys()) { auto variant = iniSettings.value(key); - if (variant.type() == QVariant::String) { + if (variant.typeId() == QMetaType::QString) { auto string = variant.toString(); if (string == "true") { variant = true; @@ -91,7 +92,7 @@ void loadOldINIFile(QSettings& settings) { } QStringList splitArgs(const QString& string, int idx) { - int length = string.length(); + qsizetype length = string.length(); Q_ASSERT(length > 0); Q_ASSERT(string.at(idx) == QLatin1Char('(')); Q_ASSERT(string.at(length - 1) == QLatin1Char(')')); @@ -120,42 +121,44 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) { for (auto it = map.cbegin(); it != map.cend(); ++it) { auto& key = it.key(); auto& variant = it.value(); - auto variantType = variant.type(); + auto variantType = variant.typeId(); // Switch some types so they are readable/modifiable in the json file - if (variantType == QVariant(1.0f).type()) { // float - variantType = QVariant::Double; + if (variantType == QMetaType::Float) { // float + variantType = QMetaType::Double; } - if (variantType == QVariant((quint16)0).type()) { // uint16 - variantType = QVariant::UInt; + if (variantType == QMetaType::UShort) { // uint16 + variantType = QMetaType::UInt; } - if (variantType == QVariant::Url) { // QUrl - variantType = QVariant::String; + if (variantType == QMetaType::QUrl) { // QUrl + variantType = QMetaType::QString; + } + + if (!variant.isValid()) { + object.insert(key, QJsonValue()); + continue; } switch (variantType) { // QML has problems with QVariant::Hash - case QVariant::Hash: { + case QMetaType::QVariantHash: { qCritical() << "Unsupported variant type" << variant.typeName() << ";" << key << variant; Q_ASSERT(false); break; } - case QVariant::Invalid: - object.insert(key, QJsonValue()); - break; - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::Int: - case QVariant::UInt: - case QVariant::Bool: - case QVariant::Double: - case QVariant::Map: - case QVariant::List: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Bool: + case QMetaType::Double: + case QMetaType::QVariantMap: + case QMetaType::QVariantList: object.insert(key, QJsonValue::fromVariant(variant)); break; - case QVariant::String: { + case QMetaType::QString: { QString result = variant.toString(); if (result.startsWith(QLatin1Char('@'))) { result.prepend(QLatin1Char('@')); @@ -164,7 +167,7 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) { break; } - case QVariant::ByteArray: { + case QMetaType::QByteArray: { QByteArray a = variant.toByteArray(); QString result = QLatin1String("@ByteArray("); int sz = a.size(); @@ -177,7 +180,7 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) { object.insert(key, result); break; } - case QVariant::Rect: { + case QMetaType::QRect: { QRect r = qvariant_cast(variant); QString result = QLatin1String("@Rect("); result += QString::number(r.x()); @@ -191,7 +194,7 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) { object.insert(key, result); break; } - case QVariant::Size: { + case QMetaType::QSize: { QSize s = qvariant_cast(variant); QString result = QLatin1String("@Size("); result += QString::number(s.width()); @@ -201,7 +204,7 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) { object.insert(key, result); break; } - case QVariant::Point: { + case QMetaType::QPoint: { QPoint p = qvariant_cast(variant); QString result = QLatin1String("@Point("); result += QString::number(p.x()); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 0f2bae11703..6a719a01873 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -298,7 +298,7 @@ uint64_t ShapeInfo::getHash() const { QString url = _url.toString(); if (!url.isEmpty()) { QByteArray baUrl = url.toLocal8Bit(); - uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); + uint32_t urlHash = qChecksum(baUrl); hasher.hashUint64((uint64_t)urlHash); } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 177390d053b..7bb31b974d6 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -190,7 +190,7 @@ void outputBits(unsigned char byte, QDebug* continuedDebug) { qts << qSetPadChar('0'); if (isalnum(byte)) { - qts << " (" << QString(byte) << ") : "; + qts << " (" << QString(QChar(byte)) << ") : "; } else { qts << " (0x" << Qt::hex << qSetFieldWidth(2) << byte << qSetFieldWidth(0) << "): "; } @@ -752,9 +752,9 @@ QString formatSecondsElapsed(float seconds) { } else { result += " day "; } - result += QDateTime::fromTime_t(rest).toUTC().toString("h 'hours' m 'minutes' s 'seconds'"); + result += QDateTime::fromSecsSinceEpoch(rest).toUTC().toString("h 'hours' m 'minutes' s 'seconds'"); } else { - result = QDateTime::fromTime_t(seconds).toUTC().toString("h 'hours' m 'minutes' s 'seconds'"); + result = QDateTime::fromSecsSinceEpoch(seconds).toUTC().toString("h 'hours' m 'minutes' s 'seconds'"); } return result; } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index c40cae5f766..653856bddd1 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "NumericalConstants.h" // When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows @@ -188,8 +189,7 @@ QString formatSecondsElapsed(float seconds); bool similarStrings(const QString& stringA, const QString& stringB); template -uint qHash(const std::shared_ptr& ptr, uint seed = 0) -{ +size_t qHash(const std::shared_ptr& ptr, size_t seed = 0) { return qHash(ptr.get(), seed); } diff --git a/libraries/shared/src/ShutdownEventListener.cpp b/libraries/shared/src/ShutdownEventListener.cpp index 6f043cd43e6..53e90e8aa34 100644 --- a/libraries/shared/src/ShutdownEventListener.cpp +++ b/libraries/shared/src/ShutdownEventListener.cpp @@ -39,7 +39,7 @@ ShutdownEventListener::ShutdownEventListener(QObject* parent) : QObject(parent) } -bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) { +bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, qintptr* result) { #ifdef Q_OS_WIN if (eventType == "windows_generic_MSG") { MSG* message = (MSG*)msg; diff --git a/libraries/shared/src/ShutdownEventListener.h b/libraries/shared/src/ShutdownEventListener.h index 8a9363e2c84..7076aff70e2 100644 --- a/libraries/shared/src/ShutdownEventListener.h +++ b/libraries/shared/src/ShutdownEventListener.h @@ -20,7 +20,7 @@ class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter public: static ShutdownEventListener& getInstance(); - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, qintptr* result) override; private: ShutdownEventListener(QObject* parent = 0); }; diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index 42de117e670..db77493872d 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -11,6 +11,7 @@ #ifndef hifi_ThreadHelpers_h #define hifi_ThreadHelpers_h +#include #include #include diff --git a/libraries/shared/src/Trace.cpp b/libraries/shared/src/Trace.cpp index e9e77b55ae2..0cdf0096043 100644 --- a/libraries/shared/src/Trace.cpp +++ b/libraries/shared/src/Trace.cpp @@ -84,7 +84,7 @@ void TraceEvent::writeJson(QTextStream& out) const { QJsonObject ev { { "name", QJsonValue(name) }, { "cat", category.categoryName() }, - { "ph", QString(type) }, + { "ph", QString(static_cast(type)) }, { "ts", timestamp }, { "pid", processID }, { "tid", threadID } diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 54dfea77cf8..7abd45c442c 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -41,8 +41,8 @@ static int cameraModeId = qRegisterMetaType(); class Camera : public QObject { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(glm::vec<3,float,glm::packed_highp> position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::qua orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString NOTIFY modeUpdated) Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT) Q_PROPERTY(bool captureMouse READ getCaptureMouse WRITE setCaptureMouse NOTIFY captureMouseChanged) @@ -88,7 +88,7 @@ public slots: * @function Camera.getPosition * @returns {Vec3} The current camera position. */ - glm::vec3 getPosition() const { return _position; } + glm::vec<3,float,glm::packed_highp> getPosition() const { return _position; } /*@jsdoc * Sets the camera position. You can also set the position using the {@link Camera|Camera.position} property. Only works if @@ -96,7 +96,7 @@ public slots: * @function Camera.setPosition * @param {Vec3} position - The position to set the camera at. */ - void setPosition(const glm::vec3& position); + void setPosition(const glm::vec<3,float,glm::packed_highp>& position); /*@jsdoc * Gets the current camera orientation. You can also get the orientation using the {@link Camera|Camera.orientation} @@ -104,7 +104,7 @@ public slots: * @function Camera.getOrientation * @returns {Quat} The current camera orientation. */ - glm::quat getOrientation() const { return _orientation; } + glm::qua getOrientation() const { return _orientation; } /*@jsdoc * Sets the camera orientation. You can also set the orientation using the {@link Camera|Camera.orientation} property. Only @@ -112,7 +112,7 @@ public slots: * @function Camera.setOrientation * @param {Quat} orientation - The orientation to set the camera to. */ - void setOrientation(const glm::quat& orientation); + void setOrientation(const glm::qua& orientation); /*@jsdoc * Gets the current mouse capture state. @@ -202,7 +202,7 @@ public slots: * * Controller.mousePressEvent.connect(onMousePressEvent); */ - void lookAt(const glm::vec3& position); + void lookAt(const glm::vec<3,float,glm::packed_highp>& position); /*@jsdoc * Sets the camera to continue looking at the specified position even while the camera moves. Only works if @@ -210,7 +210,7 @@ public slots: * @function Camera.keepLookingAt * @param {Vec3} position - The position to keep looking at. */ - void keepLookingAt(const glm::vec3& position); + void keepLookingAt(const glm::vec<3,float,glm::packed_highp>& position); /*@jsdoc * Stops the camera from continually looking at the position that was set with {@link Camera.keepLookingAt}. diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp index d467aed0f86..f16bdc6ebc8 100644 --- a/libraries/shared/src/shared/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "FileUtils.h" #include "NetworkUtils.h" diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 7dbf5461427..11657210f9a 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -132,7 +133,7 @@ QString FileUtils::standardPath(QString subfolder) { #ifdef Q_OS_ANDROID QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); #else - QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QString path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); #endif if (!subfolder.startsWith("/")) { subfolder.prepend("/"); diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index 298a1ea85d6..d151f4241d2 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -30,7 +30,7 @@ T glmFromJson(const QJsonValue& json) { T result; if (json.isArray()) { QJsonArray array = json.toArray(); - auto length = std::min(array.size(), result.length()); + auto length = std::min(array.size(), static_cast(result.length())); for (auto i = 0; i < length; ++i) { result[i] = (float)array[i].toDouble(); } @@ -136,7 +136,7 @@ void qObjectFromJsonValue(const QJsonValue& j, QObject& o) { qObjectFromJsonValue(it.value(), *child); } } else { - o.setProperty(key.c_str(), it.value()); + o.setProperty(key.c_str(), it.value().toVariant()); } } } diff --git a/libraries/shared/src/shared/PlatformHelper.h b/libraries/shared/src/shared/PlatformHelper.h index 760eb7bd420..5896c2ab4bf 100644 --- a/libraries/shared/src/shared/PlatformHelper.h +++ b/libraries/shared/src/shared/PlatformHelper.h @@ -12,6 +12,7 @@ #include #include +#include #include "../DependencyManager.h" class PlatformHelper : public QObject, public Dependency { diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 880d285f815..9c8c0daf67a 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -18,6 +18,11 @@ #include "../Profile.h" +// Macros for these are missing from QT6 +// QT6TODO: replace QGenericArgument and QGenericReturnArgument with modern equivalents +#define Q_GENERIC_ARG(type, data) QArgument(#type, data) +#define Q_GENERIC_RETURN_ARG(type, data) QReturnArgument(#type, data) + #if defined(Q_OS_WIN) // Enable event queue debugging //#define DEBUG_EVENT_QUEUE @@ -113,4 +118,35 @@ blockingInvokeMethod(const char* callingFunction, QObject* context, Func functio #define BLOCKING_INVOKE_METHOD(obj, member, ...) \ ::hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__) +inline QMultiHash qVariantToQMultiHash(const QVariant &variant) { + QMultiHash multiHash; + // QT6TODO: check if variant can be converted to a list + + for(QVariant item : variant.toList()) { + // QT6TODO: I'm not sure if this will work correctly + multiHash.insert(item.toList()[0].toString(), item.toList()[1]); + } + return multiHash; +} + +inline QVariant qMultiHashToQVariant(QMultiHash hash) { + QVariantList variant; + // QT6TODO: I'm not sure if this will work correctly + for(QString key : hash.keys()) { + QVariantList list; + list << key; + list << hash.values(key); + variant << QVariant(list); + } + return variant; +} + +inline QMultiHash qHashToQMultiHash(const QHash &hash) { + QMultiHash multiHash; + for(const QString &key : hash.keys()) { + multiHash.insert(key, hash.value(key)); + } + return multiHash; +} + #endif diff --git a/libraries/shared/src/shared/StringHelpers.cpp b/libraries/shared/src/shared/StringHelpers.cpp index 39ac23e510f..fbcded6937d 100644 --- a/libraries/shared/src/shared/StringHelpers.cpp +++ b/libraries/shared/src/shared/StringHelpers.cpp @@ -8,11 +8,12 @@ #include "StringHelpers.h" +#include #include /// Note: this will not preserve line breaks in the original input. QString simpleWordWrap(const QString& input, int maxCharactersPerLine) { - QStringList words = input.split(QRegExp("\\s+")); + QStringList words = input.split(QRegularExpression("\\s+")); QString output; QString currentLine; foreach(const auto& word, words) { diff --git a/libraries/shared/src/shared/platform/WinHelper.cpp b/libraries/shared/src/shared/platform/WinHelper.cpp index 3de82af0872..ca67c049aef 100644 --- a/libraries/shared/src/shared/platform/WinHelper.cpp +++ b/libraries/shared/src/shared/platform/WinHelper.cpp @@ -16,9 +16,7 @@ class WinHelper : public PlatformHelper, public QAbstractNativeEventFilter { public: - WinHelper() { - QAbstractEventDispatcher::instance()->installNativeEventFilter(this); - } + WinHelper() { QAbstractEventDispatcher::instance()->installNativeEventFilter(this); } ~WinHelper() { auto eventDispatcher = QAbstractEventDispatcher::instance(); @@ -27,7 +25,7 @@ class WinHelper : public PlatformHelper, public QAbstractNativeEventFilter { } } - bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override { + bool nativeEventFilter(const QByteArray& eventType, void* message, qintptr*) override { MSG* msg = static_cast(message); if (msg->message == WM_POWERBROADCAST) { switch (msg->wParam) { diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index 785f94d4285..5313daf3705 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -50,9 +50,18 @@ template class PersistentConfig : public C { _default = toJsonValue(*this).toObject().toVariantMap(); - _presets.unite(list.toVariantMap()); + auto listMap = list.toVariantMap(); + // QT6TODO: I'm not sure about this. + //_presets.unite(listMap); + for (auto it = listMap.cbegin(); it != listMap.cend(); it++) { + if (_presets.contains(it.key())) { + _presets[it.key()] = it.value(); + } else { + _presets.insert(it.key(), it.value()); + } + } if (C::isEnabled()) { - _presets.insert(DEFAULT, _default); + _presets.insert(DEFAULT, toJsonValue(*this).toObject().toVariantMap()); } _presets.insert(NONE, QVariantMap{{ "enabled", false }}); @@ -69,8 +78,9 @@ template class PersistentConfig : public C { _preset.set(preset); if (_presets.contains(preset)) { // Always start back at default to remain deterministic + // QT6TODO: I'm not sure about maps and multimaps here. Shouldn't there be one entry per key? QVariantMap config = _default; - QVariantMap presetConfig = _presets[preset].toMap(); + QVariantMap presetConfig = _presets.value(preset).toMap(); for (auto it = presetConfig.cbegin(); it != presetConfig.cend(); it++) { config.insert(it.key(), it.value()); } @@ -79,8 +89,8 @@ template class PersistentConfig : public C { } protected: - QMultiMap _default; - QMultiMap _presets; + QVariantMap _default; + QVariantMap _presets; Setting::Handle _preset; }; diff --git a/libraries/task/src/task/Varying.h b/libraries/task/src/task/Varying.h index 686a00446bd..7f9e42c6c9d 100644 --- a/libraries/task/src/task/Varying.h +++ b/libraries/task/src/task/Varying.h @@ -32,7 +32,10 @@ class Varying { template Varying(const T& data, const std::string& name = "noname") : _concept(std::make_shared>(data, name)) {} template bool canCast() const { return !!std::dynamic_pointer_cast>(_concept); } - template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } + template const T& get() const { + Q_ASSERT(std::dynamic_pointer_cast>(_concept)); + return std::static_pointer_cast>(_concept)->_data; + } template T& edit() { return std::static_pointer_cast>(_concept)->_data; } const std::string name() const { return _concept->name(); } diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp index 0c71d4fa69a..f277c20eb49 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp @@ -7,6 +7,7 @@ // #include "PluginContainer.h" +#include #include #include #include diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 4056d5026dc..ab8975e0e84 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,9 +1,9 @@ # Copyright 2013-2018, High Fidelity, Inc. -# Copyright 2020-2023 Overte e.V. +# Copyright 2020-2025 Overte e.V. # SPDX-License-Identifier: Apache-2.0 set(TARGET_NAME ui) -setup_hifi_library(OpenGL Multimedia Network Qml Quick WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) +setup_hifi_library(OpenGL Multimedia Network Qml Quick WebChannel WebSockets Xml WebEngineQuick GuiPrivate ${PLATFORM_QT_COMPONENTS}) link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers script-engine) include_hifi_library_headers(controllers) diff --git a/libraries/ui/src/DesktopPreviewProvider.h b/libraries/ui/src/DesktopPreviewProvider.h index b0cc64245f4..c6c57949c5a 100644 --- a/libraries/ui/src/DesktopPreviewProvider.h +++ b/libraries/ui/src/DesktopPreviewProvider.h @@ -8,6 +8,7 @@ #include #include +#include #include class DesktopPreviewProvider : public QObject, public Dependency { diff --git a/libraries/ui/src/InfoView.cpp b/libraries/ui/src/InfoView.cpp index c14ff6bf64a..e9d73dcef26 100644 --- a/libraries/ui/src/InfoView.cpp +++ b/libraries/ui/src/InfoView.cpp @@ -13,7 +13,7 @@ #include #include -#include +//#include #include const QUrl InfoView::QML{ "InfoView.qml" }; const QString InfoView::NAME{ "InfoView" }; @@ -34,12 +34,14 @@ void InfoView::registerType() { } QString fetchVersion(const QUrl& url) { - QXmlQuery query; - query.bindVariable("file", QVariant(url)); - query.setQuery("string((doc($file)//input[@id='version'])[1]/@value)"); - QString r; - query.evaluateTo(&r); - return r.trimmed(); + // QT6TODO: QXmlQuery is not available in Qt6 + //QXmlQuery query; + //query.bindVariable("file", QVariant(url)); + //query.setQuery("string((doc($file)//input[@id='version'])[1]/@value)"); + //QString r; + //query.evaluateTo(&r); + //return r.trimmed(); + return {}; } void InfoView::show(const QString& path, bool firstOrChangedOnly, QString urlQuery) { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 72320252f59..e9d84bec17b 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -30,6 +30,8 @@ #include "ui/ToolbarScriptingInterface.h" #include +#include + #include "MainWindow.h" /*@jsdoc @@ -119,14 +121,30 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { return false; } -static QTouchDevice _touchDevice; +static std::shared_ptr _touchDevice; + OffscreenUi::OffscreenUi() { static std::once_flag once; std::call_once(once, [&] { - _touchDevice.setCapabilities(QTouchDevice::Position); - _touchDevice.setType(QTouchDevice::TouchScreen); - _touchDevice.setName("OffscreenUiTouchDevice"); - _touchDevice.setMaximumTouchPoints(4); + // QT6TODO: choose the best parameters for this: + /*_touchDevice = std::make_shared( + "OffscreenUiTouchDevice", + 1, + QInputDevice::DeviceType::AllDevices, + QPointingDevice::PointerType::AllPointerTypes, + QInputDevice::Capability::All, + 4, //maxPoints + 2 // buttonCount + );*/ + _touchDevice = std::make_shared( + "OffscreenUiTouchDevice", + 1, + QInputDevice::DeviceType::Mouse, + QPointingDevice::PointerType::Cursor, + QInputDevice::Capability::None, + 1, //maxPoints + 2 // buttonCount + ); }); auto pointerManager = DependencyManager::get(); @@ -140,15 +158,15 @@ OffscreenUi::OffscreenUi() { } void OffscreenUi::hoverBeginEvent(const PointerEvent& event) { - OffscreenQmlSurface::hoverBeginEvent(event, _touchDevice); + OffscreenQmlSurface::hoverBeginEvent(event, *_touchDevice); } void OffscreenUi::hoverEndEvent(const PointerEvent& event) { - OffscreenQmlSurface::hoverEndEvent(event, _touchDevice); + OffscreenQmlSurface::hoverEndEvent(event, *_touchDevice); } void OffscreenUi::handlePointerEvent(const PointerEvent& event) { - OffscreenQmlSurface::handlePointerEvent(event, _touchDevice); + OffscreenQmlSurface::handlePointerEvent(event, *_touchDevice); } QObject* OffscreenUi::getFlags() { @@ -214,8 +232,8 @@ bool OffscreenUi::isPointOnDesktopWindow(QVariant point) { if (_desktop) { QVariant result; BLOCKING_INVOKE_METHOD(_desktop, "isPointOnWindow", - Q_RETURN_ARG(QVariant, result), - Q_ARG(QVariant, point)); + Q_GENERIC_RETURN_ARG(QVariant, result), + Q_GENERIC_ARG(QVariant, point)); return result.toBool(); } return false; @@ -319,12 +337,12 @@ QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& ti if (QThread::currentThread() != thread()) { QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton; BLOCKING_INVOKE_METHOD(this, "messageBox", - Q_RETURN_ARG(QMessageBox::StandardButton, result), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QString, text), - Q_ARG(QMessageBox::StandardButtons, buttons), - Q_ARG(QMessageBox::StandardButton, defaultButton)); + Q_GENERIC_RETURN_ARG(QMessageBox::StandardButton, result), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QString, text), + Q_GENERIC_ARG(QMessageBox::StandardButtons, buttons), + Q_GENERIC_ARG(QMessageBox::StandardButton, defaultButton)); return result; } @@ -335,12 +353,12 @@ ModalDialogListener* OffscreenUi::asyncMessageBox(Icon icon, const QString& titl if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "asyncMessageBox", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QString, text), - Q_ARG(QMessageBox::StandardButtons, buttons), - Q_ARG(QMessageBox::StandardButton, defaultButton)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QString, text), + Q_GENERIC_ARG(QMessageBox::StandardButtons, buttons), + Q_GENERIC_ARG(QMessageBox::StandardButton, defaultButton)); return ret; } @@ -473,11 +491,11 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q if (QThread::currentThread() != thread()) { QVariant result; BLOCKING_INVOKE_METHOD(this, "inputDialog", - Q_RETURN_ARG(QVariant, result), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QString, label), - Q_ARG(QVariant, current)); + Q_GENERIC_RETURN_ARG(QVariant, result), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QString, label), + Q_GENERIC_ARG(QVariant, current)); return result; } @@ -488,11 +506,11 @@ ModalDialogListener* OffscreenUi::inputDialogAsync(const Icon icon, const QStrin if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "inputDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QString, label), - Q_ARG(QVariant, current)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QString, label), + Q_GENERIC_ARG(QVariant, current)); return ret; } @@ -506,10 +524,10 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c if (QThread::currentThread() != thread()) { QVariant result; BLOCKING_INVOKE_METHOD(this, "customInputDialog", - Q_RETURN_ARG(QVariant, result), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QVariantMap, config)); + Q_GENERIC_RETURN_ARG(QVariant, result), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QVariantMap, config)); return result; } @@ -526,10 +544,10 @@ ModalDialogListener* OffscreenUi::customInputDialogAsync(const Icon icon, const if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "customInputDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(Icon, icon), - Q_ARG(QString, title), - Q_ARG(QVariantMap, config)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(Icon, icon), + Q_GENERIC_ARG(QString, title), + Q_GENERIC_ARG(QVariantMap, config)); return ret; } @@ -547,14 +565,14 @@ void OffscreenUi::togglePinned() { } void OffscreenUi::setPinned(bool pinned) { - bool invokeResult = _desktop && QMetaObject::invokeMethod(_desktop, "setPinned", Q_ARG(QVariant, pinned)); + bool invokeResult = _desktop && QMetaObject::invokeMethod(_desktop, "setPinned", Q_GENERIC_ARG(QVariant, pinned)); if (!invokeResult) { qWarning() << "Failed to set window visibility"; } } void OffscreenUi::setConstrainToolbarToCenterX(bool constrained) { - bool invokeResult = _desktop && QMetaObject::invokeMethod(_desktop, "setConstrainToolbarToCenterX", Q_ARG(QVariant, constrained)); + bool invokeResult = _desktop && QMetaObject::invokeMethod(_desktop, "setConstrainToolbarToCenterX", Q_GENERIC_ARG(QVariant, constrained)); if (!invokeResult) { qWarning() << "Failed to set toolbar constraint"; } @@ -814,12 +832,12 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "fileOpenDialog", - Q_RETURN_ARG(QString, result), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(QString, result), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return result; } @@ -836,12 +854,12 @@ ModalDialogListener* OffscreenUi::fileOpenDialogAsync(const QString& caption, co if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "fileOpenDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return ret; } @@ -858,12 +876,12 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "fileSaveDialog", - Q_RETURN_ARG(QString, result), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(QString, result), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return result; } @@ -882,12 +900,12 @@ ModalDialogListener* OffscreenUi::fileSaveDialogAsync(const QString& caption, co if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "fileSaveDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return ret; } @@ -906,12 +924,12 @@ QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QStri if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialog", - Q_RETURN_ARG(QString, result), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(QString, result), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return result; } @@ -928,12 +946,12 @@ ModalDialogListener* OffscreenUi::existingDirectoryDialogAsync(const QString& ca if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return ret; } @@ -1068,12 +1086,12 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir, if (QThread::currentThread() != thread()) { QString result; BLOCKING_INVOKE_METHOD(this, "assetOpenDialog", - Q_RETURN_ARG(QString, result), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(QString, result), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return result; } @@ -1091,12 +1109,12 @@ ModalDialogListener* OffscreenUi::assetOpenDialogAsync(const QString& caption, c if (QThread::currentThread() != thread()) { ModalDialogListener* ret; BLOCKING_INVOKE_METHOD(this, "assetOpenDialogAsync", - Q_RETURN_ARG(ModalDialogListener*, ret), - Q_ARG(QString, caption), - Q_ARG(QString, dir), - Q_ARG(QString, filter), - Q_ARG(QString*, selectedFilter), - Q_ARG(QFileDialog::Options, options)); + Q_GENERIC_RETURN_ARG(ModalDialogListener*, ret), + Q_GENERIC_ARG(QString, caption), + Q_GENERIC_ARG(QString, dir), + Q_GENERIC_ARG(QString, filter), + Q_GENERIC_ARG(QString*, selectedFilter), + Q_GENERIC_ARG(QFileDialog::Options, options)); return ret; } @@ -1135,13 +1153,25 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: { - QMouseEvent* mouseEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); + QMouseEvent* mouseEvent = dynamic_cast(event); + Q_ASSERT(mouseEvent); + QPointF transformedPos = mapToVirtualScreen(mouseEvent->position()); // FIXME: touch events are always being accepted. Use mouse events on the OffScreenUi for now, and investigate properly switching to touch events // (using handlePointerEvent) later - QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); + QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->globalPosition(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers(), _touchDevice.get()); mappedEvent.ignore(); + mappedEvent.setTimestamp(mouseEvent->timestamp()); + QMutableEventPoint::setPosition(mappedEvent.point(0), transformedPos); + QMutableEventPoint::setScenePosition(mappedEvent.point(0), transformedPos); + QMutableEventPoint::setTimestamp(mappedEvent.point(0), mouseEvent->timestamp()); if (QCoreApplication::sendEvent(getWindow(), &mappedEvent)) { + + // QT6TODO: I added this as a fix for Qt6 but is it needed? + // Mouse release events are always accepted for some reason, so a workaround is needed. + //if (event->type() == QEvent::MouseButtonRelease) { + // return false; + //} + return mappedEvent.isAccepted(); } break; diff --git a/libraries/ui/src/QmlFragmentClass.cpp b/libraries/ui/src/QmlFragmentClass.cpp index e3f31557023..3eaca595134 100644 --- a/libraries/ui/src/QmlFragmentClass.cpp +++ b/libraries/ui/src/QmlFragmentClass.cpp @@ -45,7 +45,7 @@ ScriptValue QmlFragmentClass::internal_constructor(ScriptContext* context, Scrip Q_ASSERT(retVal); if (QThread::currentThread() != qApp->thread()) { retVal->moveToThread(qApp->thread()); - BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_ARG(QVariantMap, properties)); + BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_GENERIC_ARG(QVariantMap, properties)); } else { retVal->initQml(properties); } diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 6542bc750f0..1987da14a8a 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -40,7 +40,7 @@ ScriptValue QmlWebWindowClass::internal_constructor(ScriptContext* context, Scri QString QmlWebWindowClass::getURL() { if (QThread::currentThread() != thread()) { QString result; - BLOCKING_INVOKE_METHOD(this, "getURL", Q_RETURN_ARG(QString, result)); + BLOCKING_INVOKE_METHOD(this, "getURL", Q_GENERIC_RETURN_ARG(QString, result)); return result; } diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 74734fdc43c..821cadb1304 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -80,7 +80,7 @@ ScriptValue QmlWindowClass::internal_constructor(ScriptContext* context, ScriptE Q_ASSERT(retVal); if (QThread::currentThread() != qApp->thread()) { retVal->moveToThread(qApp->thread()); - BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_ARG(QVariantMap, properties)); + BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_GENERIC_ARG(QVariantMap, properties)); } else { retVal->initQml(properties); } @@ -200,7 +200,7 @@ void QmlWindowClass::emitWebEvent(const QVariant& webMessage) { const QString RAISE_KEYBOARD = "_RAISE_KEYBOARD"; const QString RAISE_KEYBOARD_NUMERIC = "_RAISE_KEYBOARD_NUMERIC"; const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD"; - QString messageString = webMessage.type() == QVariant::String ? webMessage.toString() : ""; + QString messageString = webMessage.typeId() == QMetaType::QString ? webMessage.toString() : ""; if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) { QQuickItem *quickItem = asQuickItem(); if (quickItem) { @@ -265,7 +265,7 @@ void QmlWindowClass::setVisible(bool visible) { bool QmlWindowClass::isVisible() { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "isVisible", Q_RETURN_ARG(bool, result)); + BLOCKING_INVOKE_METHOD(this, "isVisible", Q_GENERIC_RETURN_ARG(bool, result)); return result; } @@ -286,7 +286,7 @@ bool QmlWindowClass::isVisible() { glm::vec2 QmlWindowClass::getPosition() { if (QThread::currentThread() != thread()) { vec2 result; - BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(glm::vec2, result)); + BLOCKING_INVOKE_METHOD(this, "getPosition", Q_GENERIC_RETURN_ARG(glm::vec2, result)); return result; } @@ -327,7 +327,7 @@ glm::vec2 toGlm(const QSizeF& size) { glm::vec2 QmlWindowClass::getSize() { if (QThread::currentThread() != thread()) { vec2 result; - BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(glm::vec2, result)); + BLOCKING_INVOKE_METHOD(this, "getSize", Q_GENERIC_RETURN_ARG(glm::vec2, result)); return result; } diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 76a5be0c2d0..ebdf5b31fa9 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -50,8 +50,8 @@ class ScriptEngine; // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping class QmlWindowClass : public QObject { Q_OBJECT - Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged) - Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> position READ getPosition WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(glm::vec<2,float,glm::packed_highp> size READ getSize WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) private: @@ -102,14 +102,14 @@ public slots: * @function OverlayWindow.getPosition * @returns {Vec2} The position of the window, in pixels. */ - glm::vec2 getPosition(); + glm::vec<2,float,glm::packed_highp> getPosition(); /*@jsdoc * Sets the position of the window, from a {@link Vec2}. * @function OverlayWindow.setPosition * @param {Vec2} position - The position of the window, in pixels. */ - void setPosition(const glm::vec2& position); + void setPosition(const glm::vec<2,float,glm::packed_highp>& position); /*@jsdoc * Sets the position of the window, from a pair of numbers. @@ -125,14 +125,14 @@ public slots: * @function OverlayWindow.getSize * @returns {Vec2} The size of the window interior, in pixels. */ - glm::vec2 getSize(); + glm::vec<2,float,glm::packed_highp> getSize(); /*@jsdoc * Sets the size of the window interior, from a {@link Vec2}. * @function OverlayWindow.setSize * @param {Vec2} size - The size of the window interior, in pixels. */ - void setSize(const glm::vec2& size); + void setSize(const glm::vec<2,float,glm::packed_highp>& size); /*@jsdoc * Sets the size of the window interior, from a pair of numbers. @@ -200,7 +200,7 @@ public slots: * // QML file, "OverlayWindow.qml". * * import QtQuick 2.5 - * import QtQuick.Controls 1.4 + * import QtQuick.Controls 2.3 * * Rectangle { * width: parent.width @@ -275,7 +275,7 @@ public slots: * @param {Vec2} position - The position of the window, in pixels. * @returns {Signal} */ - void moved(glm::vec2 position); + void moved(glm::vec<2,float,glm::packed_highp> position); /*@jsdoc * Triggered when the window changes size. diff --git a/libraries/ui/src/Tooltip.cpp b/libraries/ui/src/Tooltip.cpp index bd2c4e6d8f4..d0d87406b6d 100644 --- a/libraries/ui/src/Tooltip.cpp +++ b/libraries/ui/src/Tooltip.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index c29a71245b7..bbbf9f01bb3 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -85,7 +85,10 @@ void MenuUserData::updateQmlItemFromAction() { _qml->setProperty("text", text); _qml->setProperty("shortcut", _action->shortcut().toString()); _qml->setProperty("checked", _action->isChecked()); - _qml->setProperty("visible", _action->isVisible()); + + // Qt6 TODO: Inconsistent segfault inside Qt, possible thread race condition? + // How come it's only on "visible" and not the other properties? + //_qml->setProperty("visible", _action->isVisible()); } void MenuUserData::clear() { @@ -143,6 +146,9 @@ QObject* VrMenu::findMenuObject(const QString& menuOption) { void VrMenu::addMenu(QMenu* menu) { + auto *ui = dynamic_cast(parent()); + Q_ASSERT(ui); + QQmlEngine *engine = ui->getSurfaceContext()->engine(); Q_ASSERT(!MenuUserData::hasData(menu->menuAction())); QObject* parent = menu->parent(); QObject* qmlParent = nullptr; @@ -159,20 +165,29 @@ void VrMenu::addMenu(QMenu* menu) { Q_ASSERT(false); } QVariant returnedValue; - bool invokeResult = QMetaObject::invokeMethod(qmlParent, "addMenu", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, QVariant::fromValue(menu->title()))); - Q_ASSERT(invokeResult); - Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x - QObject* result = returnedValue.value(); - Q_ASSERT(result); - if (!result) { - qWarning() << "Unable to create QML menu for widget menu: " << menu->title(); + // QT6TODO: move to constructor, we need to load the component only once + QQmlComponent menuComponent(engine); + //menuComponent.loadFromModule("QtQuick.Controls", "Menu"); + menuComponent.loadUrl(PathUtils::qmlUrl("controls/WrappedMenu.qml")); + if (menuComponent.status() == QQmlComponent::Status::Error) { + qDebug() << "QML Menu component error: " << menuComponent.errorString(); return; } + Q_ASSERT(menuComponent.isReady()); + // QT6TODO: what deletes the item later? + QObject *menuObject = menuComponent.create(ui->getSurfaceContext()); + Q_ASSERT(menuObject); + menuObject->setObjectName(menu->title()); + menuObject->setProperty("title", menu->title()); + menuObject->setParent(qmlParent); + qDebug() << "menuObject: " << QString(menuObject->metaObject()->metaType().name()); + bool invokeResult = QMetaObject::invokeMethod(qmlParent, "addMenuWrap", Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(menuObject))); + Q_ASSERT(invokeResult); + Q_UNUSED(invokeResult); // Bind the QML and Widget together - new MenuUserData(menu->menuAction(), result, qmlParent); + new MenuUserData(menu->menuAction(), menuObject, qmlParent); } void bindActionToQmlAction(QObject* qmlAction, QAction* action, QObject* qmlParent) { @@ -191,6 +206,10 @@ void bindActionToQmlAction(QObject* qmlAction, QAction* action, QObject* qmlPare class QQuickMenuItem1; void VrMenu::addAction(QMenu* menu, QAction* action) { + auto *ui = dynamic_cast(parent()); + Q_ASSERT(ui); + QQmlEngine *engine = ui->getSurfaceContext()->engine(); + Q_ASSERT(!MenuUserData::hasData(action)); Q_ASSERT(MenuUserData::hasData(menu->menuAction())); @@ -200,20 +219,36 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { } QObject* menuQml = findMenuObject(userData->uuid.toString()); Q_ASSERT(menuQml); - QQuickMenuItem1* returnedValue { nullptr }; - bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QQuickMenuItem1*, returnedValue), - Q_ARG(QString, action->text())); + + QQmlComponent menuItemComponent(engine); + menuItemComponent.loadFromModule("QtQuick.Controls", "MenuItem"); + + if (menuItemComponent.status() == QQmlComponent::Status::Error) { + qDebug() << "QML Menu component error: " << menuItemComponent.errorString(); + return; + } + Q_ASSERT(menuItemComponent.isReady()); + // QT6TODO: I think parent deletes item later? Are there extra options for ownership? + QObject *menuItemObject = menuItemComponent.create(ui->getSurfaceContext()); + Q_ASSERT(menuItemObject); + menuItemObject->setObjectName(action->text()); + menuItemObject->setProperty("text", action->text()); + menuItemObject->setParent(menuQml); + + bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItemWrap", Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(menuItemObject))); Q_ASSERT(invokeResult); - Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x - QObject* result = reinterpret_cast(returnedValue); // returnedValue.value(); - Q_ASSERT(result); + Q_UNUSED(invokeResult); // Bind the QML and Widget together - bindActionToQmlAction(result, action, _rootMenu); + bindActionToQmlAction(menuItemObject, action, _rootMenu); } void VrMenu::addSeparator(QMenu* menu) { + auto *ui = dynamic_cast(parent()); + Q_ASSERT(ui); + QQmlEngine *engine = ui->getSurfaceContext()->engine(); + Q_ASSERT(MenuUserData::hasData(menu->menuAction())); MenuUserData* userData = MenuUserData::forObject(menu->menuAction()); if (!userData) { @@ -222,12 +257,31 @@ void VrMenu::addSeparator(QMenu* menu) { QObject* menuQml = findMenuObject(userData->uuid.toString()); Q_ASSERT(menuQml); - bool invokeResult = QMetaObject::invokeMethod(menuQml, "addSeparator", Qt::DirectConnection); + QQmlComponent menuSeparatorComponent(engine); + menuSeparatorComponent.loadFromModule("QtQuick.Controls", "MenuSeparator"); + + if (menuSeparatorComponent.status() == QQmlComponent::Status::Error) { + qDebug() << "QML Menu component error: " << menuSeparatorComponent.errorString(); + return; + } + Q_ASSERT(menuSeparatorComponent.isReady()); + QObject *menuSeparatorObject = menuSeparatorComponent.create(ui->getSurfaceContext()); + Q_ASSERT(menuSeparatorObject); + menuSeparatorObject->setParent(menuQml); + qDebug() << "VrMenu::addSeparator menuQml " << menuQml->objectName(); + qDebug() << "VrMenu::addSeparator menuQml type " << menuQml->metaObject()->className(); + + bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItemWrap", Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(menuSeparatorObject))); Q_ASSERT(invokeResult); - Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x + Q_UNUSED(invokeResult); } void VrMenu::insertAction(QAction* before, QAction* action) { + auto *ui = dynamic_cast(parent()); + Q_ASSERT(ui); + QQmlEngine *engine = ui->getSurfaceContext()->engine(); + QObject* beforeQml{ nullptr }; { MenuUserData* beforeUserData = MenuUserData::forObject(before); @@ -238,23 +292,40 @@ void VrMenu::insertAction(QAction* before, QAction* action) { beforeQml = findMenuObject(beforeUserData->uuid.toString()); } QObject* menu = beforeQml->parent(); - QQuickMenuItem1* returnedValue { nullptr }; + Q_ASSERT(menu); + + // QT6TODO: move to constructor, we need to load the component only once + QQmlComponent menuItemComponent(engine); + menuItemComponent.loadFromModule("QtQuick.Controls", "MenuItem"); + //menuComponent.loadUrl(PathUtils::qmlUrl("controls/WrappedMenu.qml")); + if (menuItemComponent.status() == QQmlComponent::Status::Error) { + qDebug() << "QML Menu component error: " << menuItemComponent.errorString(); + return; + } + Q_ASSERT(menuItemComponent.isReady()); + // QT6TODO: what deletes the item later? + QObject *menuItemObject = menuItemComponent.create(ui->getSurfaceContext()); + Q_ASSERT(menuItemObject); + menuItemObject->setObjectName(action->text()); + menuItemObject->setProperty("text", action->text()); + menuItemObject->setParent(menu); + + qDebug() << "menuObject: " << QString(menuItemObject->metaObject()->metaType().name()); + bool invokeResult = QMetaObject::invokeMethod(menu, "addItemWrap", Qt::DirectConnection, + Q_ARG(QVariant, QVariant::fromValue(menuItemObject))); // FIXME this needs to find the index of the beforeQml item and call insertItem(int, object) - bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QQuickMenuItem1*, returnedValue), - Q_ARG(QString, action->text())); Q_ASSERT(invokeResult); - QObject* result = reinterpret_cast(returnedValue); // returnedValue.value(); - Q_ASSERT(result); - if ( result ) { - bindActionToQmlAction(result, action, _rootMenu); + + if (menuItemObject) { + bindActionToQmlAction(menuItemObject, action, _rootMenu); } else { - qWarning() << "Failed to find addItem() method in object " << menu << ". Not inserting action " << action; + qWarning() << "Failed to find addItemWrap() method in object " << menu << ". Not inserting action " << action; } } class QQuickMenuBase; class QQuickMenu1; +class QQuickMenu; void VrMenu::removeAction(QAction* action) { if (!action) { diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index 6e8557e1d24..a112c8d6a62 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -247,8 +247,8 @@ void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { if (thread() != QThread::currentThread()) { BLOCKING_INVOKE_METHOD(this, "setIsOptionChecked", - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); + Q_GENERIC_ARG(const QString&, menuOption), + Q_GENERIC_ARG(bool, isChecked)); return; } QAction* menu = _actionHash.value(menuOption); @@ -595,7 +595,7 @@ QAction* MenuWrapper::addAction(const QString& menuName) { } QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { - QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); + QAction* action = _realMenu->addAction(menuName, shortcut, receiver, member); auto offscreenUi = DependencyManager::get(); if (offscreenUi) { offscreenUi->addMenuInitializer([=, this](VrMenu* vrMenu) { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 958722e3bea..9e3abbca32a 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -14,7 +14,8 @@ #include #include -#include +// QT6TODO +//#include #include #include #include @@ -27,10 +28,13 @@ #include #include #include -#include -#include +// QT6TODO +//#include +// QT6TODO +//#include #include #include +#include #include #include #include @@ -45,6 +49,7 @@ #include #include +#include #include #include @@ -112,6 +117,7 @@ class AudioHandler : public QObject, QRunnable { private: QString _newTargetDevice; + std::shared_ptr _audioOutput; QSharedPointer _surface; std::vector _players; }; @@ -194,34 +200,39 @@ AudioHandler::AudioHandler(OffscreenQmlSurface* surface, const QString& deviceNa } void AudioHandler::run() { + for (auto player : _players) { - auto mediaState = player->state(); - QMediaService* svc = player->service(); + auto mediaState = player->playbackState(); + /*QMediaService* svc = player->service(); if (nullptr == svc) { continue; - } - QAudioOutputSelectorControl* out = + }*/ + /*QAudioOutputSelectorControl* out = qobject_cast(svc->requestControl(QAudioOutputSelectorControl_iid)); if (nullptr == out) { continue; - } - QString deviceOuput; - auto outputs = out->availableOutputs(); - for (int i = 0; i < outputs.size(); i++) { - QString output = outputs[i]; - QString description = out->outputDescription(output); - if (description == _newTargetDevice) { - deviceOuput = output; + }*/ + //auto audioClient = DependencyManager::get(); + //auto outputs = audioClient->getAudioDevices(QAudioDevice::Output); + auto outputs = QMediaDevices::audioOutputs(); + QAudioDevice selectedDevice; + for (const auto &output: outputs) { + if (output.description() == _newTargetDevice) { + selectedDevice = output; break; } } - out->setActiveOutput(deviceOuput); - svc->releaseControl(out); + auto oldAudioOutput = _audioOutput; + _audioOutput = std::make_shared(); + _audioOutput->setDevice(selectedDevice); + // Q6TODO: we could set volume here in the future. + player->setAudioOutput(_audioOutput.get()); + //svc->releaseControl(out); // if multimedia was paused, it will start playing automatically after changing audio device // this will reset it back to a paused state - if (mediaState == QMediaPlayer::State::PausedState) { + if (mediaState == QMediaPlayer::PlaybackState::PausedState) { player->pause(); - } else if (mediaState == QMediaPlayer::State::StoppedState) { + } else if (mediaState == QMediaPlayer::PlaybackState::StoppedState) { player->stop(); } } @@ -355,8 +366,8 @@ void OffscreenQmlSurface::onRootCreated() { getSurfaceContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); // Connect with the audio client and listen for audio device changes - connect(DependencyManager::get().data(), &AudioClient::deviceChanged, this, [this](QAudio::Mode mode, const HifiAudioDeviceInfo& device) { - if (mode == QAudio::Mode::AudioOutput) { + connect(DependencyManager::get().data(), &AudioClient::deviceChanged, this, [this](QAudioDevice::Mode mode, const HifiAudioDeviceInfo& device) { + if (mode == QAudioDevice::Mode::Output) { QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, Q_ARG(QString, device.deviceName())); } }); @@ -424,7 +435,7 @@ unsigned int OffscreenQmlSurface::deviceIdByTouchPoint(qreal x, qreal y) { auto mapped = getRootItem()->mapFromGlobal(QPoint(x, y)); for (auto pair : _activeTouchPoints) { - if (mapped.x() == (int)pair.second.touchPoint.pos().x() && mapped.y() == (int)pair.second.touchPoint.pos().y()) { + if (mapped.x() == (int)pair.second.touchPoint.position().x() && mapped.y() == (int)pair.second.touchPoint.position().y()) { return pair.first; } } @@ -449,7 +460,7 @@ PointerEvent::EventType OffscreenQmlSurface::choosePointerEventType(QEvent::Type } } -void OffscreenQmlSurface::hoverBeginEvent(const PointerEvent& event, class QTouchDevice& device) { +void OffscreenQmlSurface::hoverBeginEvent(const PointerEvent& event, class QPointingDevice& device) { #if defined(DISABLE_QML) return; #endif @@ -457,7 +468,7 @@ void OffscreenQmlSurface::hoverBeginEvent(const PointerEvent& event, class QTouc _activeTouchPoints[event.getID()].hovering = true; } -void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QTouchDevice& device) { +void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QPointingDevice& device) { #if defined(DISABLE_QML) return; #endif @@ -473,7 +484,7 @@ void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QTouchD } } -bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release) { +bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QPointingDevice& device, bool release) { #if defined(DISABLE_QML) return false; #endif @@ -485,13 +496,14 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT QPointF windowPoint(event.getPos2D().x, event.getPos2D().y); - Qt::TouchPointState state = Qt::TouchPointStationary; + //Qt::TouchPointState state = Qt::TouchPointStationary; + QEventPoint::State state = QEventPoint::State::Stationary; if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { - state = Qt::TouchPointPressed; + state = QEventPoint::State::Pressed; } else if (event.getType() == PointerEvent::Release && event.getButton() == PointerEvent::PrimaryButton) { - state = Qt::TouchPointReleased; - } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].touchPoint.pos()) { - state = Qt::TouchPointMoved; + state = QEventPoint::State::Released; + } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].touchPoint.position()) { + state = QEventPoint::State::Updated; } // Remove the touch point if: @@ -499,7 +511,7 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT // - this was a release event and we aren't still hovering auto touchPoint = _activeTouchPoints.find(event.getID()); bool removeTouchPoint = - release || (touchPoint != _activeTouchPoints.end() && !touchPoint->second.hovering && state == Qt::TouchPointReleased); + release || (touchPoint != _activeTouchPoints.end() && !touchPoint->second.hovering && state == QEventPoint::State::Released); QEvent::Type touchType = QEvent::TouchUpdate; if (_activeTouchPoints.empty()) { // If the first active touch point is being created, send a begin @@ -510,37 +522,42 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT } { - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(state); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); + // Synthetic touch events aren't documented anywhere, + // and I don't think Qt really expects you to create them. + // + // qtbase/tests/auto/widgets/util/qscroller/tst_qscroller.cpp + // tst_QScroller::kineticScroll + QTouchEvent::TouchPoint point(0); + QMutableEventPoint::setState(point, QEventPoint::State::Pressed); + QMutableEventPoint::setPosition(point, windowPoint); + QMutableEventPoint::setScenePosition(point, windowPoint); + QMutableEventPoint::setGlobalPosition(point, windowPoint); + QMutableEventPoint::setId(point, event.getID()); + QMutableEventPoint::setState(point, state); + QMutableEventPoint::setTimestamp(point, QDateTime::currentMSecsSinceEpoch()); + QMutableEventPoint::setGlobalLastPosition(point, touchPoint->second.touchPoint.globalPosition()); + QMutableEventPoint::setGlobalPressPosition(point, touchPoint->second.touchPoint.globalPosition()); + _activeTouchPoints[event.getID()].touchPoint = point; - if (state == Qt::TouchPointPressed) { + if (state == QEventPoint::State::Pressed) { _activeTouchPoints[event.getID()].pressed = true; - } else if (state == Qt::TouchPointReleased) { + QMutableEventPoint::setPressure(_activeTouchPoints[event.getID()].touchPoint, 1.0); + } else if (state == QEventPoint::State::Released) { _activeTouchPoints[event.getID()].pressed = false; + QMutableEventPoint::setPressure(_activeTouchPoints[event.getID()].touchPoint, 0); } } - QTouchEvent touchEvent(touchType, &device, event.getKeyboardModifiers()); - { - QList touchPoints; - Qt::TouchPointStates touchPointStates; - for (const auto& entry : _activeTouchPoints) { - touchPointStates |= entry.second.touchPoint.state(); - touchPoints.push_back(entry.second.touchPoint); - } - - touchEvent.setDevice(&device); - touchEvent.setWindow(getWindow()); - touchEvent.setTarget(getRootItem()); - touchEvent.setTouchPoints(touchPoints); - touchEvent.setTouchPointStates(touchPointStates); - touchEvent.setTimestamp((ulong)QDateTime::currentMSecsSinceEpoch()); - touchEvent.ignore(); + QList touchPoints; + for (const auto& entry : _activeTouchPoints) { + touchPoints.push_back(entry.second.touchPoint); } + QTouchEvent touchEvent(touchType, &device, event.getKeyboardModifiers(), touchPoints); + + touchEvent.setTimestamp(QDateTime::currentMSecsSinceEpoch()); + touchEvent.ignore(); + // Send mouse events to the surface so that HTML dialog elements work with mouse press and hover. // // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will @@ -557,9 +574,10 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT bool eventSent = false; bool eventsAccepted = true; - if (event.getType() == PointerEvent::Move) { + // QT6TODO: I think this is not needed? + /*if (event.getType() == PointerEvent::Move) { QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, - event.getKeyboardModifiers()); + event.getKeyboardModifiers(), &device); // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install // need to investigate into why this crash is happening. //_qmlContext->setContextProperty("lastMousePosition", windowPoint); @@ -568,7 +586,7 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT eventSent = true; eventsAccepted &= mouseEvent.isAccepted(); } - } + }*/ if (touchType == QEvent::TouchBegin) { _touchBeginAccepted = QCoreApplication::sendEvent(getWindow(), &touchEvent); @@ -585,7 +603,7 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT if (event.getType() == PointerEvent::Scroll) { auto scroll = event.getScroll() * POINTEREVENT_SCROLL_SENSITIVITY; - QWheelEvent wheelEvent(windowPoint, windowPoint, QPoint(), QPoint(scroll.x, scroll.y), buttons, event.getKeyboardModifiers(), Qt::ScrollPhase::NoScrollPhase, false); + QWheelEvent wheelEvent(windowPoint, windowPoint, QPoint(), QPoint(scroll.x, scroll.y), buttons, event.getKeyboardModifiers(), Qt::ScrollPhase::NoScrollPhase, false, Qt::MouseEventSynthesizedByApplication, &device); if (QCoreApplication::sendEvent(getWindow(), &wheelEvent)) { eventSent = true; eventsAccepted &= wheelEvent.isAccepted(); @@ -606,8 +624,10 @@ void OffscreenQmlSurface::focusDestroyed(QObject* obj) { void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) { clearFocusItem(); - QQuickItem* item = static_cast(object); + QQuickItem* item = qobject_cast(object); if (!item) { + // QT6TODO: investigate when this happens and if it's a problem or not + qDebug() << "OffscreenQmlSurface::onFocusObjectChanged object is not QQuickItem"; setFocusText(false); return; } @@ -720,7 +740,7 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n if (!android || hmd) { // if HMD is being worn, allow keyboard to open. allow it to close, HMD or not. if (!raised || hmd) { - QQuickItem* item = dynamic_cast(object); + QQuickItem* item = qobject_cast(object); if (!item) { return; } @@ -772,7 +792,7 @@ void OffscreenQmlSurface::emitWebEvent(const QVariant& message) { const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD"; const QString RAISE_KEYBOARD_NUMERIC_PASSWORD = "_RAISE_KEYBOARD_NUMERIC_PASSWORD"; const QString RAISE_KEYBOARD_PASSWORD = "_RAISE_KEYBOARD_PASSWORD"; - QString messageString = message.type() == QVariant::String ? message.toString() : ""; + QString messageString = message.typeId() == QMetaType::QString ? message.toString() : ""; if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) { bool numeric = (messageString == RAISE_KEYBOARD_NUMERIC || messageString == RAISE_KEYBOARD_NUMERIC_PASSWORD); bool passwordField = (messageString == RAISE_KEYBOARD_PASSWORD || messageString == RAISE_KEYBOARD_NUMERIC_PASSWORD); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 07358dcf386..9914b057e14 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -81,9 +81,9 @@ private slots: void onFocusObjectChanged(QObject* newFocus) override; public slots: - void hoverBeginEvent(const PointerEvent& event, class QTouchDevice& device); - void hoverEndEvent(const PointerEvent& event, class QTouchDevice& device); - bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release = false); + void hoverBeginEvent(const PointerEvent& event, QPointingDevice& device); + void hoverEndEvent(const PointerEvent& event, QPointingDevice& device); + bool handlePointerEvent(const PointerEvent& event, QPointingDevice& device, bool release = false); void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); void forceHtmlAudioOutputDeviceUpdate(); void forceQmlAudioOutputDeviceUpdate(); diff --git a/libraries/ui/src/ui/QmlWrapper.cpp b/libraries/ui/src/ui/QmlWrapper.cpp index 3879e1bc8f4..2cb65bf2f08 100644 --- a/libraries/ui/src/ui/QmlWrapper.cpp +++ b/libraries/ui/src/ui/QmlWrapper.cpp @@ -38,7 +38,7 @@ void QmlWrapper::writeProperties(QVariant propertyMap) { QVariant QmlWrapper::readProperty(const QString& propertyName) { if (QThread::currentThread() != thread()) { QVariant result; - BLOCKING_INVOKE_METHOD(this, "readProperty", Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName)); + BLOCKING_INVOKE_METHOD(this, "readProperty", Q_GENERIC_RETURN_ARG(QVariant, result), Q_GENERIC_ARG(QString, propertyName)); return result; } @@ -48,7 +48,7 @@ QVariant QmlWrapper::readProperty(const QString& propertyName) { QVariant QmlWrapper::readProperties(const QVariant& propertyList) { if (QThread::currentThread() != thread()) { QVariant result; - BLOCKING_INVOKE_METHOD(this, "readProperties", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList)); + BLOCKING_INVOKE_METHOD(this, "readProperties", Q_GENERIC_RETURN_ARG(QVariant, result), Q_GENERIC_ARG(QVariant, propertyList)); return result; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 42af68f820f..5cbfa0c8576 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -188,7 +188,7 @@ ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() { TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { TabletProxy* tabletProxy = nullptr; if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "getTablet", Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId)); + BLOCKING_INVOKE_METHOD(this, "getTablet", Q_GENERIC_RETURN_ARG(TabletProxy*, tabletProxy), Q_GENERIC_ARG(QString, tabletId)); return tabletProxy; } @@ -441,7 +441,7 @@ void TabletProxy::initialScreen(const QVariant& url) { bool TabletProxy::isMessageDialogOpen() { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "isMessageDialogOpen", Q_RETURN_ARG(bool, result)); + BLOCKING_INVOKE_METHOD(this, "isMessageDialogOpen", Q_GENERIC_RETURN_ARG(bool, result)); return result; } @@ -501,7 +501,7 @@ void TabletProxy::onTabletShown() { bool TabletProxy::isPathLoaded(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "isPathLoaded", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); + BLOCKING_INVOKE_METHOD(this, "isPathLoaded", Q_GENERIC_RETURN_ARG(bool, result), Q_GENERIC_ARG(QVariant, path)); return result; } @@ -757,7 +757,7 @@ void TabletProxy::stopQMLSource() { bool TabletProxy::pushOntoStack(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "pushOntoStack", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); + BLOCKING_INVOKE_METHOD(this, "pushOntoStack", Q_GENERIC_RETURN_ARG(bool, result), Q_GENERIC_ARG(QVariant, path)); return result; } @@ -900,7 +900,7 @@ void TabletProxy::loadHTMLSourceOnTopImpl(const QString& url, const QString& inj TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) { if (QThread::currentThread() != thread()) { TabletButtonProxy* result = nullptr; - BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties)); + BLOCKING_INVOKE_METHOD(this, "addButton", Q_GENERIC_RETURN_ARG(TabletButtonProxy*, result), Q_GENERIC_ARG(QVariant, properties)); return result; } @@ -910,7 +910,7 @@ TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) { bool TabletProxy::onHomeScreen() { if (QThread::currentThread() != thread()) { bool result = false; - BLOCKING_INVOKE_METHOD(this, "onHomeScreen", Q_RETURN_ARG(bool, result)); + BLOCKING_INVOKE_METHOD(this, "onHomeScreen", Q_GENERIC_RETURN_ARG(bool, result)); return result; } @@ -919,7 +919,7 @@ bool TabletProxy::onHomeScreen() { void TabletProxy::removeButton(TabletButtonProxy* tabletButtonProxy) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "removeButton", Q_ARG(TabletButtonProxy*, tabletButtonProxy)); + QMetaObject::invokeMethod(this, "removeButton", Q_GENERIC_ARG(TabletButtonProxy*, tabletButtonProxy)); return; } @@ -928,34 +928,34 @@ void TabletProxy::removeButton(TabletButtonProxy* tabletButtonProxy) { void TabletProxy::emitScriptEvent(const QVariant& msg) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "emitScriptEvent", Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(this, "emitScriptEvent", Q_GENERIC_ARG(QVariant, msg)); return; } if (!_toolbarMode && _qmlOffscreenSurface) { - QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_GENERIC_ARG(QVariant, msg)); } else if (_toolbarMode && _desktopWindow) { - QMetaObject::invokeMethod(_desktopWindow, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(_desktopWindow, "emitScriptEvent", Qt::AutoConnection, Q_GENERIC_ARG(QVariant, msg)); } } void TabletProxy::sendToQml(const QVariant& msg) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "sendToQml", Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(this, "sendToQml", Q_GENERIC_ARG(QVariant, msg)); return; } if (!_toolbarMode && _qmlOffscreenSurface) { - QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_GENERIC_ARG(QVariant, msg)); } else if (_toolbarMode && _desktopWindow) { - QMetaObject::invokeMethod(_desktopWindow, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg)); + QMetaObject::invokeMethod(_desktopWindow, "sendToQml", Qt::AutoConnection, Q_GENERIC_ARG(QVariant, msg)); } } OffscreenQmlSurface* TabletProxy::getTabletSurface() { if (QThread::currentThread() != thread()) { OffscreenQmlSurface* result = nullptr; - BLOCKING_INVOKE_METHOD(this, "getTabletSurface", Q_RETURN_ARG(OffscreenQmlSurface*, result)); + BLOCKING_INVOKE_METHOD(this, "getTabletSurface", Q_GENERIC_RETURN_ARG(OffscreenQmlSurface*, result)); return result; } @@ -1091,7 +1091,7 @@ TabletButtonProxy::~TabletButtonProxy() { QVariantMap TabletButtonProxy::getProperties() { if (QThread::currentThread() != thread()) { QVariantMap result; - BLOCKING_INVOKE_METHOD(this, "getProperties", Q_RETURN_ARG(QVariantMap, result)); + BLOCKING_INVOKE_METHOD(this, "getProperties", Q_GENERIC_RETURN_ARG(QVariantMap, result)); return result; } @@ -1100,7 +1100,7 @@ QVariantMap TabletButtonProxy::getProperties() { void TabletButtonProxy::editProperties(const QVariantMap& properties) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "editProperties", Q_ARG(QVariantMap, properties)); + QMetaObject::invokeMethod(this, "editProperties", Q_GENERIC_ARG(QVariantMap, properties)); return; } diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.cpp b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp index 4934ba4e351..a978bd23c3a 100644 --- a/libraries/ui/src/ui/ToolbarScriptingInterface.cpp +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp @@ -83,7 +83,7 @@ ToolbarProxy::ToolbarProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qml ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) { if (QThread::currentThread() != thread()) { ToolbarButtonProxy* result = nullptr; - BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties)); + BLOCKING_INVOKE_METHOD(this, "addButton", Q_GENERIC_RETURN_ARG(ToolbarButtonProxy*, result), Q_GENERIC_ARG(QVariant, properties)); return result; } @@ -114,7 +114,7 @@ void ToolbarProxy::removeButton(const QVariant& name) { ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) { if (QThread::currentThread() != thread()) { ToolbarProxy* result = nullptr; - BLOCKING_INVOKE_METHOD(this, "getToolbar", Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId)); + BLOCKING_INVOKE_METHOD(this, "getToolbar", Q_GENERIC_RETURN_ARG(ToolbarProxy*, result), Q_GENERIC_ARG(QString, toolbarId)); return result; } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index 210e1f36b1c..c00f1d2148e 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -78,7 +78,7 @@ void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { bool ContextAwareProfile::isRestrictedGetProperty() { if (QThread::currentThread() != thread()) { bool restrictedResult = false; - BLOCKING_INVOKE_METHOD(this, "isRestrictedGetProperty", Q_RETURN_ARG(bool, restrictedResult)); + BLOCKING_INVOKE_METHOD(this, "isRestrictedGetProperty", Q_GENERIC_RETURN_ARG(bool, restrictedResult)); return restrictedResult; } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index 7ea56846992..584b54946e9 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -17,7 +17,7 @@ #include #if !defined(Q_OS_ANDROID) -#include +#include #include using ContextAwareProfileParent = QQuickWebEngineProfile; diff --git a/libraries/ui/src/ui/types/FileTypeProfile.cpp b/libraries/ui/src/ui/types/FileTypeProfile.cpp index 9fca1be4361..f2ac9aa4f50 100644 --- a/libraries/ui/src/ui/types/FileTypeProfile.cpp +++ b/libraries/ui/src/ui/types/FileTypeProfile.cpp @@ -33,7 +33,7 @@ FileTypeProfile::FileTypeProfile(QQmlContext* parent) : setOffTheRecord(false); auto requestInterceptor = new RequestInterceptor(this); - setRequestInterceptor(requestInterceptor); + setUrlRequestInterceptor(requestInterceptor); std::lock_guard lock(FileTypeProfile_mutex); FileTypeProfile_instances.insert(this); diff --git a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp index 3f2d50e8fa8..7a5c668e59f 100644 --- a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp +++ b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp @@ -30,7 +30,7 @@ HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent) setOffTheRecord(false); // we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user - setRequestInterceptor(new RequestInterceptor(this)); + setUrlRequestInterceptor(new RequestInterceptor(this)); std::lock_guard lock(HFWebEngineProfile_mutex); HFWebEngineProfile_instances.insert(this); diff --git a/plugins/JSAPIExample/src/JSAPIExample.cpp b/plugins/JSAPIExample/src/JSAPIExample.cpp index 02b59ba412f..49ef023131c 100644 --- a/plugins/JSAPIExample/src/JSAPIExample.cpp +++ b/plugins/JSAPIExample/src/JSAPIExample.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include // for ::settingsFilename() #include // for ::usecTimestampNow() diff --git a/plugins/hifiOsc/src/OscPlugin.cpp b/plugins/hifiOsc/src/OscPlugin.cpp index a261674feb3..3e8ba8c44af 100644 --- a/plugins/hifiOsc/src/OscPlugin.cpp +++ b/plugins/hifiOsc/src/OscPlugin.cpp @@ -85,7 +85,7 @@ enum class FaceCap { // used to mirror left/right shapes from FaceCap. // i.e. right and left shapes are swapped. -FaceCap faceMirrorMap[static_cast(FaceCap::BlendshapeCount)] = { +FaceCap faceMirrorMap[static_cast(FaceCap::BlendshapeCount)] = { FaceCap::BrowsU_C, FaceCap::BrowsD_R, FaceCap::BrowsD_L, @@ -140,7 +140,7 @@ FaceCap faceMirrorMap[static_cast(FaceCap::BlendshapeCount)] = { FaceCap::TongueOut }; -static const char* STRINGS[static_cast(FaceCap::BlendshapeCount)] = { +static const char* STRINGS[static_cast(FaceCap::BlendshapeCount)] = { "BrowsU_C", "BrowsD_L", "BrowsD_R", @@ -195,7 +195,7 @@ static const char* STRINGS[static_cast(FaceCap::BlendshapeCount)] = { "TongueOut" }; -static enum controller::StandardAxisChannel CHANNELS[static_cast(FaceCap::BlendshapeCount)] = { +static enum controller::StandardAxisChannel CHANNELS[static_cast(FaceCap::BlendshapeCount)] = { controller::BROWSU_C, controller::BROWSD_L, controller::BROWSD_R, @@ -582,7 +582,7 @@ controller::Input::NamedVector OscPlugin::InputDevice::getAvailableInputs() cons static controller::Input::NamedVector availableInputs; if (availableInputs.size() == 0) { for (int i = 0; i < static_cast(FaceCap::BlendshapeCount); i++) { - availableInputs.push_back(makePair(CHANNELS[i], QString(STRINGS[i]))); + availableInputs.push_back(makePair(static_cast(CHANNELS[i]), QString(STRINGS[i]))); } } availableInputs.push_back(makePair(controller::HEAD, QString("Head"))); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 499bbef211d..6e5d35d7f6a 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -29,6 +29,10 @@ #include #include "../../interface/src/Menu.h" +#ifdef Q_OS_WIN +#include +#endif + Q_DECLARE_LOGGING_CATEGORY(displayplugins) Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display") diff --git a/plugins/steamClient/src/SteamAPIPlugin.cpp b/plugins/steamClient/src/SteamAPIPlugin.cpp index b6e5b2ea770..0c2ee74395a 100644 --- a/plugins/steamClient/src/SteamAPIPlugin.cpp +++ b/plugins/steamClient/src/SteamAPIPlugin.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/script-archive/floofChat/FloofChat.qml b/script-archive/floofChat/FloofChat.qml index 8d111222927..a96383880b5 100644 --- a/script-archive/floofChat/FloofChat.qml +++ b/script-archive/floofChat/FloofChat.qml @@ -1,5 +1,5 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 //import Hifi 1.0 as Hifi Rectangle { diff --git a/scripts/communityScripts/chatBubbles/chatBubbles.js b/scripts/communityScripts/chatBubbles/chatBubbles.js index df4d398ba5b..3bc39ba586d 100644 --- a/scripts/communityScripts/chatBubbles/chatBubbles.js +++ b/scripts/communityScripts/chatBubbles/chatBubbles.js @@ -14,7 +14,7 @@ const CHAT_CHANNEL = "chat"; const TYPING_NOTIFICATION_CHANNEL = "Chat-Typing"; -const CONFIG_UPDATE_CHANNEL = "ChatBubbles-Config"; +const CONFIG_UPDATE_CHANNEL = "ChatBubbles-Enabled"; const BUBBLE_MIN_LIFETIME_SECS = 10; const BUBBLE_MAX_LIFETIME_SECS = 60; // failsafe to prevent really long perma-messages @@ -33,9 +33,8 @@ const SELF_BUBBLES = false; const NOTIFY_SOUND = SoundCache.getSound(Script.resolvePath("./assets/notify.wav")); -let settings = { - enabled: true, -}; +// NOTE: we don't call Settings.setValue here because chat.js does that for us +let settings = Settings.getValue("Chat", { chatBubbles: true }); let currentBubbles = {}; let typingIndicators = {}; @@ -340,21 +339,9 @@ function ChatBubbles_HideTypingIndicator(senderID) { } function ChatBubbles_RecvMsg(channel, msg, senderID, localOnly) { - // IPC between ArmoredChat's config window and this script + // IPC between the chat config and this script if (channel === CONFIG_UPDATE_CHANNEL && localOnly) { - let data; - try { - data = JSON.parse(msg); - } catch (e) { - console.error(e); - return; - } - - for (const [key, value] of Object.entries(data)) { - settings[key] = value; - } - - Settings.setValue("ChatBubbles-Config", settings); + settings.chatBubbles = msg === "true"; return; } @@ -373,14 +360,14 @@ function ChatBubbles_RecvMsg(channel, msg, senderID, localOnly) { } if (channel === TYPING_NOTIFICATION_CHANNEL) { - if (data.action === "typing_start") { + if (data.action === "typing_start" && settings.chatBubbles) { // don't spawn a bubble if they're too far away if (Vec3.distance(MyAvatar.position, data.position) > MAX_DISTANCE) { return; } ChatBubbles_ShowTypingIndicator(senderID); } else if (data.action === "typing_stop") { ChatBubbles_HideTypingIndicator(senderID); } - } else if (data.action === "send_chat_message" && settings.enabled) { + } else if (data.action === "send_chat_message" && settings.chatBubbles) { // don't spawn a bubble if they're too far away if (data.channel !== "local") { return; } if (Vec3.distance(MyAvatar.position, data.position) > MAX_DISTANCE) { return; } @@ -433,12 +420,10 @@ Window.domainConnectionRefused.connect((_msg, _code, _info) => ChatBubbles_Delet AvatarList.avatarRemovedEvent.connect(sessionID => ChatBubbles_Delete(sessionID)); AvatarList.avatarSessionChangedEvent.connect((_, oldSessionID) => ChatBubbles_Delete(oldSessionID)); -settings = Settings.getValue("ChatBubbles-Config", settings); Messages.messageReceived.connect(ChatBubbles_RecvMsg); Messages.subscribe(TYPING_NOTIFICATION_CHANNEL); Script.scriptEnding.connect(() => { - Settings.setValue("ChatBubbles-Config", settings); Messages.messageReceived.disconnect(ChatBubbles_RecvMsg); Messages.unsubscribe(TYPING_NOTIFICATION_CHANNEL); ChatBubbles_DeleteAll(); diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index cd76b3efdf5..b5b37fb1346 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -22,35 +22,29 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/menu.js", "system/bubble.js", "system/snapshot.js", - "system/pal.js", // "system/mod.js", // older UX, if you prefer - "system/avatarapp.js", - "system/settings/settings.js", "system/makeUserConnection.js", "system/notifications.js", "system/create/edit.js", "system/dialTone.js", "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", - "system/emote.js", - "system/miniTablet.js", "system/audioMuteOverlay.js", "system/inspect.js", "system/keyboardShortcuts/keyboardShortcuts.js", "system/onEscape.js", - "system/places/places.js" //"developer/debugging/scriptMemoryReport.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ + "system/systemApps.js", + //"system/chat.js", + "system/places/places.js", "system/controllers/controllerScripts.js", "system/controllers/squeezeHands.js", "communityScripts/notificationCore/notificationCore.js", "simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js", - {"stable": "system/more/app-more.js", "beta": "https://more.overte.org/more/app-more.js"}, - "communityScripts/armored-chat/armored_chat.js", "communityScripts/chatBubbles/chatBubbles.js", "communityScripts/contextMenu.js", "system/flyCam.js", - //"system/chat.js" ]; if (Window.interstitialModeEnabled) { diff --git a/scripts/developer/debugging/debugHaze.qml b/scripts/developer/debugging/debugHaze.qml index 5a18f88c79a..039dc23023f 100644 --- a/scripts/developer/debugging/debugHaze.qml +++ b/scripts/developer/debugging/debugHaze.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../utilities/render/configSlider" Item { diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index 23708033351..eca330cb99e 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import Hifi 1.0 as Hifi diff --git a/scripts/developer/debugging/surfaceGeometryPass.qml b/scripts/developer/debugging/surfaceGeometryPass.qml index ba1db66d16e..0c17789794b 100644 --- a/scripts/developer/debugging/surfaceGeometryPass.qml +++ b/scripts/developer/debugging/surfaceGeometryPass.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "configSlider" Column { diff --git a/scripts/developer/tests/playaPerformanceTest.qml b/scripts/developer/tests/playaPerformanceTest.qml index 028cfc9e1ab..fd6fec15734 100644 --- a/scripts/developer/tests/playaPerformanceTest.qml +++ b/scripts/developer/tests/playaPerformanceTest.qml @@ -1,5 +1,5 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import Qt.labs.settings 1.0 Rectangle { diff --git a/scripts/developer/tests/textureStress.qml b/scripts/developer/tests/textureStress.qml index 1a8b9944757..eca135a1c46 100644 --- a/scripts/developer/tests/textureStress.qml +++ b/scripts/developer/tests/textureStress.qml @@ -1,5 +1,5 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 Rectangle { id: root diff --git a/scripts/developer/utilities/audio/Jitter.qml b/scripts/developer/utilities/audio/Jitter.qml index 91f197a9191..524b17ba82f 100644 --- a/scripts/developer/utilities/audio/Jitter.qml +++ b/scripts/developer/utilities/audio/Jitter.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 ColumnLayout { diff --git a/scripts/developer/utilities/audio/MovingValue.qml b/scripts/developer/utilities/audio/MovingValue.qml index bbd9c31d6be..b10691867d6 100644 --- a/scripts/developer/utilities/audio/MovingValue.qml +++ b/scripts/developer/utilities/audio/MovingValue.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import "../lib/plotperf" diff --git a/scripts/developer/utilities/audio/Section.qml b/scripts/developer/utilities/audio/Section.qml index 100d05f6b2f..f6bd324646d 100644 --- a/scripts/developer/utilities/audio/Section.qml +++ b/scripts/developer/utilities/audio/Section.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 Rectangle { diff --git a/scripts/developer/utilities/audio/Stats.qml b/scripts/developer/utilities/audio/Stats.qml index e2291e485d6..91a3fc63ae3 100644 --- a/scripts/developer/utilities/audio/Stats.qml +++ b/scripts/developer/utilities/audio/Stats.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/audio/Stream.qml b/scripts/developer/utilities/audio/Stream.qml index e9383b627aa..8cd944d33d6 100644 --- a/scripts/developer/utilities/audio/Stream.qml +++ b/scripts/developer/utilities/audio/Stream.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 ColumnLayout { diff --git a/scripts/developer/utilities/audio/TabletStats.qml b/scripts/developer/utilities/audio/TabletStats.qml index b50acabec48..00213b4666f 100644 --- a/scripts/developer/utilities/audio/TabletStats.qml +++ b/scripts/developer/utilities/audio/TabletStats.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/audio/Value.qml b/scripts/developer/utilities/audio/Value.qml index 70df7695bc4..9ed90b5b331 100644 --- a/scripts/developer/utilities/audio/Value.qml +++ b/scripts/developer/utilities/audio/Value.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 RowLayout { diff --git a/scripts/developer/utilities/cache/stats.qml b/scripts/developer/utilities/cache/stats.qml index d7888eb1aaf..c454ea21ff9 100644 --- a/scripts/developer/utilities/cache/stats.qml +++ b/scripts/developer/utilities/cache/stats.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../lib/plotperf" Item { diff --git a/scripts/developer/utilities/lib/jet/qml/TaskList.qml b/scripts/developer/utilities/lib/jet/qml/TaskList.qml index e4b0267d3f1..02c68b152f8 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskList.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskList.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml index a935163bd9a..e65d76ea3e1 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 6a658a33e34..1f2a9be23c2 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import "../../prop" as Prop diff --git a/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml b/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml index 0b89b5d5be1..3a2966dbe5a 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskTimeFrameView.qml @@ -11,8 +11,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls @@ -181,7 +181,8 @@ Rectangle { var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); - ctx.font="12px Verdana"; + // QT6TODO + //ctx.font="12px Verdana"; displayBackground(ctx); if (jobsArray.length > 0) { diff --git a/scripts/developer/utilities/lib/plotperf/Color.qml b/scripts/developer/utilities/lib/plotperf/Color.qml index 1ad72fe2e61..c8add4d20d5 100644 --- a/scripts/developer/utilities/lib/plotperf/Color.qml +++ b/scripts/developer/utilities/lib/plotperf/Color.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/lib/plotperf/PlotPerf.qml b/scripts/developer/utilities/lib/plotperf/PlotPerf.qml index f239cc010a5..05103f923d1 100644 --- a/scripts/developer/utilities/lib/plotperf/PlotPerf.qml +++ b/scripts/developer/utilities/lib/plotperf/PlotPerf.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 Item { id: root @@ -264,7 +264,8 @@ Item { var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); - ctx.font="12px Verdana"; + // QT6TODO + //ctx.font="12px Verdana"; displayBackground(ctx); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 75b927a2c95..0104d4ec8fd 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -10,7 +10,7 @@ // SPDX-License-Identifier: Apache-2.0 // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml index bf9089d82c9..1a02a6b770f 100644 --- a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml +++ b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/render/configSlider/RichSlider.qml b/scripts/developer/utilities/render/configSlider/RichSlider.qml index ff16cb32ad5..db6993f2930 100644 --- a/scripts/developer/utilities/render/configSlider/RichSlider.qml +++ b/scripts/developer/utilities/render/configSlider/RichSlider.qml @@ -9,8 +9,8 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 as Original -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as Original +//import QtQuick.Controls.Styles import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/render/engineList.qml b/scripts/developer/utilities/render/engineList.qml index 55df1bd1875..7aa05f8d36d 100644 --- a/scripts/developer/utilities/render/engineList.qml +++ b/scripts/developer/utilities/render/engineList.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/engineProfiler.qml b/scripts/developer/utilities/render/engineProfiler.qml index 11373187812..ecb3d0c2e49 100644 --- a/scripts/developer/utilities/render/engineProfiler.qml +++ b/scripts/developer/utilities/render/engineProfiler.qml @@ -10,7 +10,7 @@ // SPDX-License-Identifier: Apache-2.0 // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/highlight.qml b/scripts/developer/utilities/render/highlight.qml index d8af2a828ef..3a1f4098fcd 100644 --- a/scripts/developer/utilities/render/highlight.qml +++ b/scripts/developer/utilities/render/highlight.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/highlight/HighlightStyle.qml b/scripts/developer/utilities/render/highlight/HighlightStyle.qml index 475aadfdce0..511094f5975 100644 --- a/scripts/developer/utilities/render/highlight/HighlightStyle.qml +++ b/scripts/developer/utilities/render/highlight/HighlightStyle.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import "../configSlider" import "../../lib/plotperf" diff --git a/scripts/developer/utilities/render/lightClustering.qml b/scripts/developer/utilities/render/lightClustering.qml index 69cf1a6064f..75f007ed2ea 100644 --- a/scripts/developer/utilities/render/lightClustering.qml +++ b/scripts/developer/utilities/render/lightClustering.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "configSlider" import "../lib/plotperf" diff --git a/scripts/developer/utilities/render/lod.qml b/scripts/developer/utilities/render/lod.qml index 3d4cafdd38c..5ffeadd37e2 100644 --- a/scripts/developer/utilities/render/lod.qml +++ b/scripts/developer/utilities/render/lod.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/render/luci/Antialiasing.qml b/scripts/developer/utilities/render/luci/Antialiasing.qml index 7c174d53c5b..e3773dcc181 100644 --- a/scripts/developer/utilities/render/luci/Antialiasing.qml +++ b/scripts/developer/utilities/render/luci/Antialiasing.qml @@ -10,7 +10,7 @@ // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/luci/Culling.qml b/scripts/developer/utilities/render/luci/Culling.qml index 548b6908b35..b6a0bedfd36 100644 --- a/scripts/developer/utilities/render/luci/Culling.qml +++ b/scripts/developer/utilities/render/luci/Culling.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../../lib/prop" as Prop diff --git a/scripts/developer/utilities/render/rates.qml b/scripts/developer/utilities/render/rates.qml index f4a4ee2c6cb..17e95aa722e 100644 --- a/scripts/developer/utilities/render/rates.qml +++ b/scripts/developer/utilities/render/rates.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../lib/plotperf" Item { diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 6af0b21d1bf..831aa6d3b29 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/developer/utilities/render/stats.qml b/scripts/developer/utilities/render/stats.qml index 25c108884a0..c776885469e 100644 --- a/scripts/developer/utilities/render/stats.qml +++ b/scripts/developer/utilities/render/stats.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../lib/plotperf" Item { diff --git a/scripts/developer/utilities/render/statsGPU.qml b/scripts/developer/utilities/render/statsGPU.qml index 8d284c11ca8..bba61eb7f21 100644 --- a/scripts/developer/utilities/render/statsGPU.qml +++ b/scripts/developer/utilities/render/statsGPU.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../lib/plotperf" Item { diff --git a/scripts/developer/utilities/render/subsurfaceScattering.qml b/scripts/developer/utilities/render/subsurfaceScattering.qml index ec7367217ef..a4552f529ef 100644 --- a/scripts/developer/utilities/render/subsurfaceScattering.qml +++ b/scripts/developer/utilities/render/subsurfaceScattering.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "configSlider" Column { diff --git a/scripts/developer/utilities/render/surfaceGeometryPass.qml b/scripts/developer/utilities/render/surfaceGeometryPass.qml index ba1db66d16e..0c17789794b 100644 --- a/scripts/developer/utilities/render/surfaceGeometryPass.qml +++ b/scripts/developer/utilities/render/surfaceGeometryPass.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "configSlider" Column { diff --git a/scripts/developer/utilities/render/textureMonitor.qml b/scripts/developer/utilities/render/textureMonitor.qml index b01a390fa80..7e1a594ad93 100644 --- a/scripts/developer/utilities/render/textureMonitor.qml +++ b/scripts/developer/utilities/render/textureMonitor.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import "../lib/plotperf" diff --git a/scripts/developer/utilities/render/transition.qml b/scripts/developer/utilities/render/transition.qml index 440075c9496..ffb233c046c 100644 --- a/scripts/developer/utilities/render/transition.qml +++ b/scripts/developer/utilities/render/transition.qml @@ -8,10 +8,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/workload/avatars.qml b/scripts/developer/utilities/workload/avatars.qml index 5951e72c315..c4e325d841f 100644 --- a/scripts/developer/utilities/workload/avatars.qml +++ b/scripts/developer/utilities/workload/avatars.qml @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index 746a572f29b..0c5e007bc2f 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import stylesUit 1.0 diff --git a/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml b/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml index 32a4664e0ac..f1ffeabcfe8 100644 --- a/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml +++ b/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml @@ -11,7 +11,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 as HifiStylesUit import TabletScriptingInterface 1.0 import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml index 1938586edb8..5fefde32e7b 100644 --- a/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml @@ -9,7 +9,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Layouts 1.3 import stylesUit 1.0 as HifiStylesUit import TabletScriptingInterface 1.0 diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml index 2a796465aed..0cc89ed9898 100644 --- a/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml @@ -9,7 +9,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import QtQuick.Layouts 1.3 import stylesUit 1.0 as HifiStylesUit import TabletScriptingInterface 1.0 diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 2dee944a855..6730c1425fc 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -190,7 +190,7 @@ icon: "icons/tablet-icons/bubble-i.svg", activeIcon: "icons/tablet-icons/bubble-a.svg", text: buttonName, - sortOrder: 4 + sortOrder: 2 }); onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table diff --git a/scripts/system/chat.js b/scripts/system/chat.js new file mode 100644 index 00000000000..22bc3bfef0b --- /dev/null +++ b/scripts/system/chat.js @@ -0,0 +1,225 @@ +// +// chat.js +// +// Created by Ada on 2025-10-26 +// Copyright 2025 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 +// +// SPDX-License-Identifier: Apache-2.0 +"use strict"; + +const EVENT_LOG_SETTING = "chat/eventsLog"; +let eventLog = Settings.getValue(EVENT_LOG_SETTING, []); + +function sendLoggedEventToQml(data) { + // only send it the event if the window exists + if (appWindow) { + appWindow.sendToQml(JSON.stringify(data)); + } + eventLog.push(data); + Settings.setValue(EVENT_LOG_SETTING, eventLog); +} + +const SystemTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + +let settings = Settings.getValue("Chat", { + joinNotifications: true, + broadcastEnabled: false, + chatBubbles: true, + desktopWindow: false, +}); + +function updateSetting(name, value) { + switch (name) { + case "join_notify": + settings.joinNotifications = value; + break; + + case "broadcast": + settings.broadcastEnabled = value; + break; + + case "chat_bubbles": + settings.chatBubbles = value; + Messages.sendLocalMessage("ChatBubbles-Enabled", JSON.stringify(value)); + break; + + case "desktop_window": + settings.desktopWindow = value; + // FIXME: this sometimes leaves an empty ghost window on the overlay? + appWindow.presentationMode = value ? + Desktop.PresentationMode.NATIVE : + Desktop.PresentationMode.VIRTUAL; + break; + } + + Settings.setValue("Chat", settings); +} + +function sendInitialSettings() { + const send = (name, value) => appWindow.sendToQml(JSON.stringify({ + event: "change_setting", + name: name, + value: value, + })); + + send("join_notify", settings.joinNotifications); + send("broadcast", settings.broadcastEnabled); + send("chat_bubbles", settings.chatBubbles); + send("desktop_window", settings.desktopWindow); +} + +function appWindowFromQml(rawMsg) { + const msg = JSON.parse(rawMsg); + + switch (msg.event) { + case "change_setting": + updateSetting(msg.setting, msg.value); + break; + + case "clear_history": + eventLog = []; + Settings.setValue(EVENT_LOG_SETTING, eventLog); + break; + + case "send_message": + Messages.sendMessage("chat", JSON.stringify({ + action: "send_chat_message", + position: MyAvatar.position, + displayName: MyAvatar.sessionDisplayName ? MyAvatar.sessionDisplayName : MyAvatar.displayName, + message: msg.body, + channel: settings.broadcastEnabled ? "domain" : "local", + timestamp: Date.now(), + })); + break; + + case "start_typing": + Messages.sendMessage("Chat-Typing", JSON.stringify({ + action: "typing_start", + position: MyAvatar.position, + })); + break; + + case "end_typing": + Messages.sendMessage("Chat-Typing", JSON.stringify({ + action: "typing_stop" + })); + break; + } +} + +function recreateAppWindow() { + appWindow = Desktop.createWindow( + `${Script.resourcesPath()}qml/overte/chat/Chat.qml`, + { + // TODO: translation support in JS + title: "Chat", + size: { x: 550, y: 400 }, + presentationMode: settings.desktopWindow ? + Desktop.PresentationMode.NATIVE : + Desktop.PresentationMode.VIRTUAL, + additionalFlags: Desktop.ALWAYS_ON_TOP, + } + ); + + // FIXME: CLOSE_BUTTON_HIDES doesn't work with desktop windows + appWindow.closed.connect(() => { + appButton.buttonData.isActive = false; + appButton.button.editProperties({ isActive: false }); + appWindow = undefined; + }); + + appWindow.fromQml.connect(appWindowFromQml); + + sendInitialSettings(); +} + +const appButton = { + buttonData: { + isActive: false, + sortOrder: 6, + icon: "icons/tablet-icons/chat-i.svg", + activeIcon: "icons/tablet-icons/chat-a.svg", + + // TODO: translation support in JS + text: "CHAT", + }, + + button: null, + + onClicked() { + this.buttonData.isActive = !this.buttonData.isActive; + this.button.editProperties({ isActive: this.buttonData.isActive }); + + if (!appWindow) { recreateAppWindow(); } + + appWindow.visible = this.buttonData.isActive; + }, +}; + +let appWindow; + +appButton.button = SystemTablet.addButton(appButton.buttonData); +appButton.button.clicked.connect(() => appButton.onClicked()); + +Messages.subscribe("chat"); +Messages.subscribe("Chat-Typing"); + +Messages.messageReceived.connect((channel, rawMsg, senderID, _localOnly) => { + if (channel === "chat") { + const msg = JSON.parse(rawMsg); + sendLoggedEventToQml({ + event: "recv_message", + name: msg.displayName, + body: msg.message, + timestamp: msg.timestamp, + }); + } else if (channel === "Chat-Typing") { + const avatar = AvatarManager.getAvatar(senderID); + const msg = JSON.parse(rawMsg); + if (appWindow) { + appWindow.sendToQml(JSON.stringify({ + event: msg.action === "typing_start" ? "start_typing" : "end_typing", + name: avatar.sessionDisplayName ? avatar.sessionDisplayName : avatar.displayName, + uuid: senderID, + })); + } + } +}); + +let palData = []; + +AvatarManager.avatarAddedEvent.connect(uuid => { + // pal data isn't immediately available when an avatar joins, + // so we need a bit of a delay to wait for it to come through + Script.setTimeout(() => { + palData = AvatarManager.getPalData().data; + + const name = palData.find(e => e.sessionUUID === uuid)?.sessionDisplayName; + + // still couldn't get anything to show, don't bother + if (!name) { return; } + + sendLoggedEventToQml({ + event: "user_joined", + name: name, + timestamp: Date.now(), + }); + }, 1500); +}); + +AvatarManager.avatarRemovedEvent.connect(uuid => { + const name = palData.find(e => e.sessionUUID === uuid)?.sessionDisplayName; + + sendLoggedEventToQml({ + event: "user_left", + name: name, + timestamp: Date.now(), + }); +}); + +Script.scriptEnding.connect(() => { + SystemTablet.removeButton(appButton.button); +}); diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 0eccec01a6b..cb8aefb5ab5 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -59,6 +59,7 @@ }; this.adjustReticleDepth = function(controllerData) { + // QT6TODO: Reticle.isPointingAtSystemOverlay does not work currently if (Reticle.isPointingAtSystemOverlay(Reticle.position)) { var reticlePositionOnHUD = HMD.worldPointFromOverlay(Reticle.position); Reticle.depth = Vec3.distance(reticlePositionOnHUD, HMD.position); diff --git a/scripts/system/create/entityList/qml/EditEntityList.qml b/scripts/system/create/entityList/qml/EditEntityList.qml index 1d5beb9914f..55be0d8212a 100644 --- a/scripts/system/create/entityList/qml/EditEntityList.qml +++ b/scripts/system/create/entityList/qml/EditEntityList.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 2.2 import QtWebChannel 1.0 import controls 1.0 import hifi.toolbars 1.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import controlsUit 1.0 as HifiControls import stylesUit 1.0 diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 091ddf0ac77..016b18da340 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 2.2 import QtWebChannel 1.0 import controls 1.0 import hifi.toolbars 1.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import controlsUit 1.0 as HifiControls import stylesUit 1.0 diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 87295ff543f..12e8a1f14c6 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 2.2 import QtWebChannel 1.0 import controls 1.0 import hifi.toolbars 1.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import controlsUit 1.0 as HifiControls import stylesUit 1.0 diff --git a/scripts/system/create/qml/NewEntityButton.qml b/scripts/system/create/qml/NewEntityButton.qml index a78af7a845e..fd87f9dba0e 100644 --- a/scripts/system/create/qml/NewEntityButton.qml +++ b/scripts/system/create/qml/NewEntityButton.qml @@ -1,5 +1,5 @@ import QtQuick 2.0 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import TabletScriptingInterface 1.0 Item { diff --git a/scripts/system/create/qml/NewMaterialDialog.qml b/scripts/system/create/qml/NewMaterialDialog.qml index 423afe4b186..e3a9d0b1437 100644 --- a/scripts/system/create/qml/NewMaterialDialog.qml +++ b/scripts/system/create/qml/NewMaterialDialog.qml @@ -13,7 +13,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import stylesUit 1.0 import controlsUit 1.0 diff --git a/scripts/system/create/qml/NewModelDialog.qml b/scripts/system/create/qml/NewModelDialog.qml index 5dcbfafa513..0df9b72045c 100644 --- a/scripts/system/create/qml/NewModelDialog.qml +++ b/scripts/system/create/qml/NewModelDialog.qml @@ -12,7 +12,7 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import stylesUit 1.0 import controlsUit 1.0 diff --git a/scripts/system/create/qml/NewParticleDialog.qml b/scripts/system/create/qml/NewParticleDialog.qml index c07843f30ad..08f22403dba 100644 --- a/scripts/system/create/qml/NewParticleDialog.qml +++ b/scripts/system/create/qml/NewParticleDialog.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import stylesUit 1.0 import controlsUit 1.0 diff --git a/scripts/system/create/qml/NewPolyVoxDialog.qml b/scripts/system/create/qml/NewPolyVoxDialog.qml index 38a8d5d77e3..34cf253ad83 100644 --- a/scripts/system/create/qml/NewPolyVoxDialog.qml +++ b/scripts/system/create/qml/NewPolyVoxDialog.qml @@ -13,7 +13,7 @@ // import QtQuick 2.5 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import stylesUit 1.0 import controlsUit 1.0 diff --git a/scripts/system/create/qml/NewSoundDialog.qml b/scripts/system/create/qml/NewSoundDialog.qml index f43a2707acc..13a8ef345bd 100644 --- a/scripts/system/create/qml/NewSoundDialog.qml +++ b/scripts/system/create/qml/NewSoundDialog.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Dialogs as OriginalDialogs import stylesUit 1.0 import controlsUit 1.0 diff --git a/scripts/system/placePortals.js b/scripts/system/placePortals.js new file mode 100644 index 00000000000..adb976080fc --- /dev/null +++ b/scripts/system/placePortals.js @@ -0,0 +1,204 @@ +// +// placePortals.js +// +// Created by Ada on 2025-11-07 +// Copyright 2025 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 +// +// SPDX-License-Identifier: Apache-2.0 +"use strict"; + +const PORTAL_LIFETIME_SECS = 20; +const MESSAGE_CHANNEL = "org.overte.PlacePortal"; + +// convenience function because QML can't use the vec3 or quat we normally use +const CREATE_MESSAGE_CHANNEL = "org.overte.PlacePortal.Create"; + +const ROOT_DEFAULT_PROPS = { + type: "Shape", + shape: "Cylinder", + grab: { grabbable: false }, + ignorePickIntersection: true, + dimensions: [1, 2, 1], + alpha: 0, +}; + +const VISUAL_DEFAULT_PROPS = { + type: "ParticleEffect", + // FIXME: why is this oriented weird? + emitDimensions: [0.5, 0.5, 1.5], + textures: Script.resolvePath("./places/icons/portalFX.png"), + emitRate: 100, + lifespan: 3, + maxParticles: 500, + polarStart: 0, + polarFinish: Math.PI, + emitAcceleration: [0, 0, 0], + radiusStart: 1.0, + particleRadius: 0.5, + radiusFinish: 0.3, + alphaStart: 0.1, + alpha: 0.1, + alphaFinish: 0.1, + emitSpeed: -0.1, + speedSpread: 0, + colorStart: [255, 0, 0], + color: [255, 0, 0], + colorFinish: [255, 255, 255], +}; + +const TITLE_DEFAULT_PROPS = { + type: "Text", + grab: { grabbable: false }, + ignorePickIntersection: true, + dimensions: [2, 0.5, 0.1], + localPosition: [0, 1, 0], + billboardMode: "yaw", + backgroundAlpha: 0.0, + lineHeight: 0.1, + unlit: true, + textColor: "white", + textEffect: "outline fill", + textEffectColor: "black", + textEffectThickness: 0.3, + alignment: "center", +}; + +// key is the UUID of the collider, +// value is { +// titleEntity: UUID, +// visualEntity: UUID, +// placeUrl: string, +// onTick: function, +// lifetime: int, +// tickInterval: setInterval, +// } +let portalInfo = new Map(); + +function deleteAllPortals() { + for (const [id, props] of portalInfo) { + Entities.deleteEntity(id); + Entities.deleteEntity(props.titleEntity); + } + + portalInfo.clear(); +} + +function colorHash(x) { + const FNV_PRIME = 0x01000193; + const FNV_OFFSET = 0x811c9dc5; + + let value = FNV_OFFSET; + for (const c of x) { + value ^= c; + value = (value * FNV_PRIME) & 0xffffffff; + } + value /= 0x7fffffff; + + const TAU = 2 * Math.PI; + + const r = 127 * Math.sin((value - (1 / 3)) * TAU) + 128; + const g = 127 * Math.sin((value + 0) * TAU) + 128; + const b = 127 * Math.sin((value + (1 / 3)) * TAU) + 128; + + return [r, g, b]; +} + +function createPortal(placeName, placeUrl, position) { + const color = colorHash(placeName); + + // TODO: translation support + const goingToText = `Going to ${placeName}`; + + const root = Entities.addEntity({ + position: Vec3.sum(position, [0, 1, 0]), + // enterEntity can't be connected to through Entities, is that a bug? + script: `(function(){ + this.enterEntity = _id => { + Window.displayAnnouncement(${JSON.stringify(goingToText)}); + location.handleLookupString(${JSON.stringify(placeUrl)}); + }; +})`, + ...ROOT_DEFAULT_PROPS, + }, "local"); + + const title = Entities.addEntity({ + parentID: root, + text: `${placeName}\n${PORTAL_LIFETIME_SECS}`, + ...TITLE_DEFAULT_PROPS, + }, "local"); + + const visual = Entities.addEntity({ + parentID: root, + ...VISUAL_DEFAULT_PROPS, + colorStart: color, + color: color, + }, "local"); + + let props = { + rootEntity: root, + titleEntity: title, + visualEntity: visual, + placeUrl: placeUrl, + + lifetime: PORTAL_LIFETIME_SECS, + + onTick() { + this.lifetime -= 1; + Entities.editEntity(this.titleEntity, { text: `${placeName}\n${this.lifetime}` }); + + if (this.lifetime <= 0) { + portalInfo.delete(this.rootEntity); + Script.clearInterval(this.tickInterval); + + Entities.deleteEntity(this.titleEntity); + Entities.deleteEntity(this.visualEntity); + Entities.deleteEntity(this.rootEntity); + } + }, + }; + + props.tickInterval = Script.setInterval(() => props.onTick(), 1000); + + portalInfo.set(root, props); +} + +Messages.messageReceived.connect((channel, rawMsg, _senderID, _localOnly) => { + if (channel === MESSAGE_CHANNEL) { + try { + const data = JSON.parse(rawMsg); + + if (data.place_name && data.place_url && data.position) { + createPortal(data.place_name, data.place_url, data.position); + } else { + console.warn(MESSAGE_CHANNEL, "Necessary data missing, can't create portal"); + } + } catch (e) { console.error(e); } + } else if (channel === CREATE_MESSAGE_CHANNEL) { + try { + const data = JSON.parse(rawMsg); + + if (data.place_name && data.place_url) { + createPortal( + data.place_name, + data.place_url, + Vec3.sum( + MyAvatar.feetPosition, + Vec3.multiply( + 1.5 * MyAvatar.sensorToWorldScale, + Quat.getForward(MyAvatar.orientation) + ) + ) + ); + } else { + console.warn(MESSAGE_CHANNEL, "Necessary data missing, can't create portal"); + } + } catch (e) { console.error(e); } + } +}); + +Messages.subscribe(MESSAGE_CHANNEL); +Script.scriptEnding.connect(() => deleteAllPortals()); +Window.domainChanged.connect(_url => deleteAllPortals()); diff --git a/scripts/system/settings/qml/AdvancedOptions.qml b/scripts/system/settings/qml/AdvancedOptions.qml index c1d38a0c9dd..cbd2bff3c81 100644 --- a/scripts/system/settings/qml/AdvancedOptions.qml +++ b/scripts/system/settings/qml/AdvancedOptions.qml @@ -1,6 +1,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.5 -import QtQuick.Controls.Styles 1.4 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 Item { diff --git a/scripts/system/settings/qml/SettingBoolean.qml b/scripts/system/settings/qml/SettingBoolean.qml index 541f1f98555..ad415d67cb7 100644 --- a/scripts/system/settings/qml/SettingBoolean.qml +++ b/scripts/system/settings/qml/SettingBoolean.qml @@ -88,7 +88,7 @@ Item { hoverEnabled: true; propagateComposedEvents: true; - onPressed: { + onPressed: mouse => { mouse.accepted = false } diff --git a/scripts/system/settings/qml/SettingComboBox.qml b/scripts/system/settings/qml/SettingComboBox.qml index fca268be8a1..1a482b131e7 100644 --- a/scripts/system/settings/qml/SettingComboBox.qml +++ b/scripts/system/settings/qml/SettingComboBox.qml @@ -152,7 +152,7 @@ Item { hoverEnabled: true; propagateComposedEvents: true; - onPressed: { + onPressed: mouse => { if (disabled) return; mouse.accepted = false; } @@ -202,4 +202,4 @@ Item { function setOptionIndex(index) { control.currentIndex = index; } -} \ No newline at end of file +} diff --git a/scripts/system/settings/qml/SettingNumber.qml b/scripts/system/settings/qml/SettingNumber.qml index aa00a3f3c47..2a71ba4be9a 100644 --- a/scripts/system/settings/qml/SettingNumber.qml +++ b/scripts/system/settings/qml/SettingNumber.qml @@ -1,6 +1,6 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.5 -import QtQuick.Controls.Styles 1.4 +import QtQuick +import QtQuick.Controls +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 Item { @@ -67,7 +67,7 @@ Item { width: parent.width; clip: true; font.pixelSize: 22 - validator: RegExpValidator { regExp: /[0-9]*/ } + validator: RegularExpressionValidator { regularExpression: /[0-9]*/ } background: Rectangle { color: "#111"; @@ -162,7 +162,7 @@ Item { hoverEnabled: true; propagateComposedEvents: true; - onPressed: { + onPressed: mouse => { mouse.accepted = false } @@ -184,4 +184,4 @@ Item { } } } -} \ No newline at end of file +} diff --git a/scripts/system/settings/qml/SettingSlider.qml b/scripts/system/settings/qml/SettingSlider.qml index 05093724423..b8ca1cb3c3d 100644 --- a/scripts/system/settings/qml/SettingSlider.qml +++ b/scripts/system/settings/qml/SettingSlider.qml @@ -1,6 +1,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.5 -import QtQuick.Controls.Styles 1.4 +//import QtQuick.Controls.Styles import QtQuick.Layouts 1.3 Item { @@ -119,7 +119,7 @@ Item { hoverEnabled: true; propagateComposedEvents: true; - onPressed: { + onPressed: mouse => { mouse.accepted = false } @@ -139,4 +139,4 @@ Item { } } } -} \ No newline at end of file +} diff --git a/scripts/system/settings/qml/pages/GraphicsSettings.qml b/scripts/system/settings/qml/pages/GraphicsSettings.qml index c1796ba1805..2a3a87353fd 100644 --- a/scripts/system/settings/qml/pages/GraphicsSettings.qml +++ b/scripts/system/settings/qml/pages/GraphicsSettings.qml @@ -1,4 +1,4 @@ -import QtQuick 2.15 +import QtQuick import QtQuick.Controls 2.15 import QtQuick.Layouts 1.3 import "../" @@ -74,7 +74,7 @@ Flickable { optionIndex: Performance.getPerformancePreset() - 1; options: ["Low Power", "Low", "Medium", "High", "Custom"]; - onValueChanged: { + function onValueChanged(index) { Performance.setPerformancePreset(index + 1); if (index !== 4) switchToAGraphicsPreset(); } @@ -230,7 +230,7 @@ Flickable { options: ["Economical", "Interactive", "Real-Time", "Custom"]; optionIndex: Performance.getRefreshRateProfile(); - onValueChanged: { + function onValueChanged(index) { Performance.setRefreshRateProfile(index); fpsAdvancedOptions.isEnabled = index == 3; } @@ -247,7 +247,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(0) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(0, value); } } @@ -259,7 +259,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(1) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(1, value); } } @@ -271,7 +271,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(2) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(2, value); } } @@ -283,7 +283,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(3) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(3, value); } } @@ -295,7 +295,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(4) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(4, value); } } @@ -307,7 +307,7 @@ Flickable { suffixText: "fps"; settingValue: Performance.getCustomRefreshRate(5) - onValueChanged: { + function onValueChanged(value) { Performance.setCustomRefreshRate(5, value); } } diff --git a/scripts/system/systemApps.js b/scripts/system/systemApps.js new file mode 100644 index 00000000000..5ae59e5b199 --- /dev/null +++ b/scripts/system/systemApps.js @@ -0,0 +1,215 @@ +// +// systemApps.js +// +// Created by Ada on 2025-10-26 +// Copyright 2025 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 +// +// SPDX-License-Identifier: Apache-2.0 +"use strict"; + +const SystemTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + +function defaultOnClicked() { + this.appButtonData.isActive = !this.appButtonData.isActive; + this.appButton.editProperties({ isActive: this.appButtonData.isActive }); + + if (this.appButtonData.isActive) { + SystemTablet.loadQMLSource(this.qmlSource, false); + } else { + SystemTablet.gotoHomeScreen(); + } +} + +function defaultOnScreenChanged(type, url) { + if (type != "QML" || url != this.qmlSource) { + this.appButtonData.isActive = false; + this.appButton.editProperties({ isActive: this.appButtonData.isActive }); + } +} + +// has absolutely nothing to do with HTTP cookies, +// just here to keep track of what requests are being handled +let waitingRequestCookies = new Set(); + +function defaultFromQml(message) { + const data = JSON.parse(message); + + switch (data.action) { + // QML's XMLHttpRequest doesn't have the authentication header that's + // needed for interacting with the directory server. This passes a + // request through the JS XMLHttpRequest so it can use the account auth. + case "system:auth_request": { + // sometimes requests get sent twice? don't let that happen + if (waitingRequestCookies.has(data.data.cookie)) { + return; + } else { + waitingRequestCookies.add(data.data.cookie); + } + + let xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = () => { + if (xhr.readyState === 4 /* DONE */) { + SystemTablet.sendToQml(JSON.stringify({ + action: "system:auth_request", + data: { + status: xhr.status, + statusText: xhr.statusText, + responseText: xhr.responseText, + responseURL: data.data.url, + cookie: data.data.cookie, + }, + })); + + waitingRequestCookies.delete(data.data.cookie); + } + }; + + xhr.open(data.data.method, data.data.url); + + if (data.data.method === "POST") { + xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8"); + } + + xhr.send(data.data.body); + } break; + + case "system:location_go_back": location.goBack(); break; + case "system:location_go_forward": location.goForward(); break; + case "system:location_go_to": { + location.handleLookupString(data.data.path); + + // hide the tablet after travelling + SystemTablet.gotoHomeScreen(); + SystemTablet.tabletShown = false; + } break; + } +} + +const SYSTEM_APPS = { + settings: { + appButtonData: { + sortOrder: 3, + isActive: false, + icon: Script.resolvePath("./settings/img/icon_white.png"), + activeIcon: Script.resolvePath("./settings/img/icon_black.png"), + + // TODO: translation support in JS + text: "SETTINGS", + }, + + qmlSource: "overte/settings/Settings.qml", + appButton: null, + }, + + avatar: { + appButtonData: { + sortOrder: 4, + isActive: false, + icon: "icons/tablet-icons/avatar-i.svg", + activeIcon: "icons/tablet-icons/avatar-a.svg", + + // TODO: translation support in JS + text: "AVATAR", + }, + + qmlSource: "overte/avatar_picker/AvatarPicker.qml", + appButton: null, + }, + + contacts: { + appButtonData: { + sortOrder: 5, + isActive: false, + icon: "icons/tablet-icons/people-i.svg", + activeIcon: "icons/tablet-icons/people-a.svg", + + // TODO: translation support in JS + text: "CONTACTS", + }, + + qmlSource: "overte/contacts/ContactsList.qml", + appButton: null, + }, + + // TODO: to be picked up again in a later PR + /*places: { + appButtonData: { + sortOrder: 6, + isActive: false, + // TODO: put these somewhere more global + icon: Script.resolvePath("./places/icons/appicon_i.png"), + activeIcon: Script.resolvePath("./places/icons/appicon_a.png"), + + // TODO: translation support in JS + text: "PLACES", + }, + + qmlSource: "overte/place_picker/PlacePicker.qml", + appButton: null, + },*/ + + more: { + appButtonData: { + isActive: false, + // TODO: put these somewhere more global + icon: Script.resolvePath("./more/appicon_i.png"), + activeIcon: Script.resolvePath("./more/appicon_a.png"), + + // TODO: translation support in JS + text: "MORE", + }, + + qmlSource: "overte/more_apps/MoreApps.qml", + appButton: null, + }, +}; + +function setupButtons() { + for (let app of Object.values(SYSTEM_APPS)) { + if (!app.onClicked) { app.onClicked = defaultOnClicked; } + if (!app.onScreenChanged) { app.onScreenChanged = defaultOnScreenChanged; } + if (!app.fromQml) { app.fromQml = defaultFromQml; } + + let button = SystemTablet.addButton(app.appButtonData); + button.clicked.connect(() => app.onClicked()); + SystemTablet.screenChanged.connect((type, url) => app.onScreenChanged(type, url)); + SystemTablet.fromQml.connect(message => app.fromQml(message)); + app.appButton = button; + } + + Script.scriptEnding.connect(() => { + for (const app of Object.values(SYSTEM_APPS)) { + if (app.appButton) { + SystemTablet.removeButton(app.appButton); + } + } + }); +} + +function loadInstalledMoreApps() { + const RETRY_DELAY_SECS = 5; + const installedScripts = Settings.getValue("moreApp/installedScripts", []); + + for (const scriptUrl of installedScripts) { + ScriptDiscoveryService.loadScript(scriptUrl, false); + } + + // Check that all of the installed scripts are running. + // If there's some that aren't, then give them one more try. + Script.setTimeout(() => { + const running = ScriptDiscoveryService.getRunning(); + + for (const scriptUrl of installedScripts) { + if (!running.includes(scriptUrl)) { + ScriptDiscoveryService.loadScript(scriptUrl, false); + } + } + }, RETRY_DELAY_SECS * 1000); +} + +loadInstalledMoreApps(); +setupButtons(); diff --git a/tests-manual/controllers/qml/main.qml b/tests-manual/controllers/qml/main.qml index 66060399a67..085e5ccbc9d 100644 --- a/tests-manual/controllers/qml/main.qml +++ b/tests-manual/controllers/qml/main.qml @@ -1,7 +1,7 @@ import QtQuick 2.1 import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs ApplicationWindow { id: window diff --git a/tests-manual/qml/qml/MacQml.qml b/tests-manual/qml/qml/MacQml.qml index 14749bb8264..7df1e23a5f3 100644 --- a/tests-manual/qml/qml/MacQml.qml +++ b/tests-manual/qml/qml/MacQml.qml @@ -50,7 +50,7 @@ import QtQuick 2.2 import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 +import QtQuick.Dialogs import QtQuick.Controls 1.2 import QtWebEngine 1.5 diff --git a/tests-manual/qml/qml/main.qml b/tests-manual/qml/qml/main.qml index 3b375433939..bd0f9e19fca 100644 --- a/tests-manual/qml/qml/main.qml +++ b/tests-manual/qml/qml/main.qml @@ -50,7 +50,7 @@ import QtQuick 2.2 import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 +import QtQuick.Dialogs import QtQuick.Controls 1.2 import "qml/UI.js" as UI import "qml" diff --git a/tests-manual/ui/qml/ControlsGalleryWindow.qml b/tests-manual/ui/qml/ControlsGalleryWindow.qml index 32fd62da363..56cd0a35ed7 100644 --- a/tests-manual/ui/qml/ControlsGalleryWindow.qml +++ b/tests-manual/ui/qml/ControlsGalleryWindow.qml @@ -1,6 +1,6 @@ import QtQuick 2.0 import QtQuick.Window 2.3 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import '../../../scripts/developer/tests' as Tests ApplicationWindow { diff --git a/tests/audio/src/AudioTests.cpp b/tests/audio/src/AudioTests.cpp index 1e097855e8f..f3acc6c96e1 100644 --- a/tests/audio/src/AudioTests.cpp +++ b/tests/audio/src/AudioTests.cpp @@ -38,7 +38,7 @@ void AudioTests::listAudioDevices() { /* // AudioClient::devicesChanged is declared as: - // void devicesChanged(QAudio::Mode mode, const QList& devices); + // void devicesChanged(QAudioDevice::Mode mode, const QList& devices); // // Unfortunately with QSignalSpy we have to use the old SIGNAL() syntax, so it was a bit tricky // to figure out how to get the signal to connect. The snippet below lists signals in the format @@ -59,7 +59,7 @@ void AudioTests::listAudioDevices() { } */ - QSignalSpy spy(ac.get(), SIGNAL(devicesChanged(QAudio::Mode,QList))); + QSignalSpy spy(ac.get(), SIGNAL(devicesChanged(QAudioDevice::Mode,QList))); QVERIFY(spy.isValid()); // This checks that the signal has connected spy.wait(15000); @@ -72,7 +72,7 @@ void AudioTests::listAudioDevices() { // QSignalSpy is a QList, which stores the received signals. We can then examine it to see // what we got. for(auto event : spy) { - QAudio::Mode mode = qvariant_cast(event.at(0)); + QAudioDevice::Mode mode = qvariant_cast(event.at(0)); QList devs = qvariant_cast>(event.at(1)); QVERIFY(devs.count() > 0); diff --git a/tests/model-serializers/src/ModelSerializersTests.cpp b/tests/model-serializers/src/ModelSerializersTests.cpp index a781d84730a..bc293ae191c 100644 --- a/tests/model-serializers/src/ModelSerializersTests.cpp +++ b/tests/model-serializers/src/ModelSerializersTests.cpp @@ -171,3 +171,63 @@ void ModelSerializersTests::loadGLTF() { QVERIFY(expectWarnings == (model->loadWarningCount>0)); QVERIFY(expectErrors == (model->loadErrorCount>0)); } + +/*void ModelSerializersTests::loadFBX() { + // QTTODO: fix path, but how? + QString filename("/home/ksuprynowicz/overte/overte/interface/resources/serverless/Models/dome.fbx"); + QFile fbx_file(filename); + QVERIFY(fbx_file.open(QIODevice::ReadOnly)); + + QByteArray data = fbx_file.readAll(); + QByteArray uncompressedData; + QUrl url("https://example.com"); + + qInfo() << "URL: " << url; + + if (filename.toLower().endsWith(".gz")) { + url.setPath("/" + filename.chopped(3)); + + if (gunzip(data, uncompressedData)) { + qInfo() << "Uncompressed into" << uncompressedData.length(); + } else { + qCritical() << "Failed to uncompress"; + } + } else { + url.setPath("/" + filename); + uncompressedData = data; + } + + + ModelLoader loader; + QMultiHash serializerMapping; + std::string webMediaType; + + serializerMapping.insert("combineParts", true); + serializerMapping.insert("deduplicateIndices", true); + + qInfo() << "Loading model from" << uncompressedData.length() << "bytes data, url" << url; + + // Check that we can find a serializer for this + auto serializer = DependencyManager::get()->getSerializerForMediaType(uncompressedData, url, webMediaType); + QVERIFY(serializer); + + + + hfm::Model::Pointer model = loader.load(uncompressedData, serializerMapping, url, webMediaType); + QVERIFY(model); + + if (!model) { + // We expected this parse to fail, so nothing more to do here. + return; + } + + QVERIFY(!model->meshes.empty()); + QVERIFY(!model->joints.empty()); + + qInfo() << "Model was loaded with" << model->meshes.count() << "meshes and" << model->joints.count() << "joints. Found" << model->loadWarningCount << "warnings and" << model->loadErrorCount << "errors"; + + // Some models we test are expected to be broken. We're testing that we can load the model without blowing up, + // so loading it with errors is still a successful test. + QVERIFY(model->loadWarningCount == 0); + QVERIFY(model->loadErrorCount == 0); +}*/ diff --git a/tests/model-serializers/src/ModelSerializersTests.h b/tests/model-serializers/src/ModelSerializersTests.h index 45b913a5d7d..89a95919874 100644 --- a/tests/model-serializers/src/ModelSerializersTests.h +++ b/tests/model-serializers/src/ModelSerializersTests.h @@ -21,6 +21,7 @@ private slots: void initTestCase(); void loadGLTF_data(); void loadGLTF(); + //void loadFBX(); }; diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp index 3fac162fc11..9d03315288f 100644 --- a/tests/networking/src/ResourceTests.cpp +++ b/tests/networking/src/ResourceTests.cpp @@ -35,7 +35,7 @@ void ResourceTests::initTestCase() { const qint64 MAXIMUM_CACHE_SIZE = 1024 * 1024 * 1024; // 1GB // set up the file cache - //QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + //QString cachePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); QString cachePath = "./resourceTestCache"; QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkDiskCache* cache = new QNetworkDiskCache(); diff --git a/tests/script-engine/CMakeLists.txt b/tests/script-engine/CMakeLists.txt index 70185e5856d..6eae37a6ca4 100644 --- a/tests/script-engine/CMakeLists.txt +++ b/tests/script-engine/CMakeLists.txt @@ -6,6 +6,8 @@ macro (setup_testcase_dependencies) # V8TODO: replace most link_hifi_libraries with include_hifi_library_headers # link in the shared libraries link_hifi_libraries(shared test-utils script-engine networking octree avatars entities model-networking material-networking model-serializers graphics gpu ktx shaders hfm image procedural audio) + include_hifi_library_headers(entities) + include_hifi_library_headers(audio) package_libraries_for_deployment() diff --git a/tests/script-engine/src/ScriptEngineTests.cpp b/tests/script-engine/src/ScriptEngineTests.cpp index bde93de1c5c..871a2e6459b 100644 --- a/tests/script-engine/src/ScriptEngineTests.cpp +++ b/tests/script-engine/src/ScriptEngineTests.cpp @@ -334,7 +334,7 @@ void ScriptEngineTests::testQuat() { }); connect(sm.get(), &ScriptManager::unhandledException, [](std::shared_ptr exception){ - QVERIFY(exception->errorMessage.contains("undefined to glm::quat")); + QVERIFY(exception->errorMessage.contains("undefined to glm::qua")); }); diff --git a/tools/animedit/qml/fields/BooleanField.qml b/tools/animedit/qml/fields/BooleanField.qml index 8ba2b938554..5d2956b549f 100644 --- a/tools/animedit/qml/fields/BooleanField.qml +++ b/tools/animedit/qml/fields/BooleanField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { diff --git a/tools/animedit/qml/fields/IdField.qml b/tools/animedit/qml/fields/IdField.qml index b3ee5b6ac98..cc61982bf4a 100644 --- a/tools/animedit/qml/fields/IdField.qml +++ b/tools/animedit/qml/fields/IdField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { id: row diff --git a/tools/animedit/qml/fields/JSONField.qml b/tools/animedit/qml/fields/JSONField.qml index f7b16b82cda..8ba5bb6084f 100644 --- a/tools/animedit/qml/fields/JSONField.qml +++ b/tools/animedit/qml/fields/JSONField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Column { diff --git a/tools/animedit/qml/fields/NumberArrayField.qml b/tools/animedit/qml/fields/NumberArrayField.qml index dd393dc2590..4ac6f776ed6 100644 --- a/tools/animedit/qml/fields/NumberArrayField.qml +++ b/tools/animedit/qml/fields/NumberArrayField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { @@ -43,7 +43,7 @@ Row { // then substitue the second into the \d+ of the first, yeilding this monstrocity. // ^\[\s*(\d+\.?\d*)(\s*,\s*(\d+\.?\d*))*\]$|\[\s*\] - //validator: RegExpValidator { regExp: /^\[\s*(\d+\.?\d*)(\s*,\s*(\d+\.?\d*))*\]$|\[\s*\]/ } + //validator: RegularExpressionValidator { regularExpression: /^\[\s*(\d+\.?\d*)(\s*,\s*(\d+\.?\d*))*\]$|\[\s*\]/ } text: JSON.stringify(value) onEditingFinished: { diff --git a/tools/animedit/qml/fields/NumberField.qml b/tools/animedit/qml/fields/NumberField.qml index 88c952d2056..cfbc0a4ebcf 100644 --- a/tools/animedit/qml/fields/NumberField.qml +++ b/tools/animedit/qml/fields/NumberField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { diff --git a/tools/animedit/qml/fields/StringField.qml b/tools/animedit/qml/fields/StringField.qml index 7ce5cd4c6c0..ae01bc0a094 100644 --- a/tools/animedit/qml/fields/StringField.qml +++ b/tools/animedit/qml/fields/StringField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { diff --git a/tools/animedit/qml/fields/TypeField.qml b/tools/animedit/qml/fields/TypeField.qml index 0a9a7110745..79ee1bd9a56 100644 --- a/tools/animedit/qml/fields/TypeField.qml +++ b/tools/animedit/qml/fields/TypeField.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs Row { id: row diff --git a/tools/animedit/qml/main.qml b/tools/animedit/qml/main.qml index 2d0c277132f..aaa67b05fae 100644 --- a/tools/animedit/qml/main.qml +++ b/tools/animedit/qml/main.qml @@ -1,8 +1,8 @@ import QtQuick 2.12 import QtQuick.Window 2.12 -import QtQuick.Controls 1.6 -import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs import "fields" import "nodes" diff --git a/tools/animedit/qml/nodes/BlendDirectional.qml b/tools/animedit/qml/nodes/BlendDirectional.qml index efdea746628..32be0b97ead 100644 --- a/tools/animedit/qml/nodes/BlendDirectional.qml +++ b/tools/animedit/qml/nodes/BlendDirectional.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/BlendLinear.qml b/tools/animedit/qml/nodes/BlendLinear.qml index 5ed15d9ea78..fa55db879be 100644 --- a/tools/animedit/qml/nodes/BlendLinear.qml +++ b/tools/animedit/qml/nodes/BlendLinear.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/BlendLinearMove.qml b/tools/animedit/qml/nodes/BlendLinearMove.qml index cde0c83fd73..b32edb59ac6 100644 --- a/tools/animedit/qml/nodes/BlendLinearMove.qml +++ b/tools/animedit/qml/nodes/BlendLinearMove.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/ClipData.qml b/tools/animedit/qml/nodes/ClipData.qml index 99490643936..c616dba3283 100644 --- a/tools/animedit/qml/nodes/ClipData.qml +++ b/tools/animedit/qml/nodes/ClipData.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/DefaultPose.qml b/tools/animedit/qml/nodes/DefaultPose.qml index eb16dfb0c0c..315629fa151 100644 --- a/tools/animedit/qml/nodes/DefaultPose.qml +++ b/tools/animedit/qml/nodes/DefaultPose.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/InverseKinematics.qml b/tools/animedit/qml/nodes/InverseKinematics.qml index b401869c32e..75395ea8082 100644 --- a/tools/animedit/qml/nodes/InverseKinematics.qml +++ b/tools/animedit/qml/nodes/InverseKinematics.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/Manipulator.qml b/tools/animedit/qml/nodes/Manipulator.qml index 6a651ae73b7..4e2a3fd912d 100644 --- a/tools/animedit/qml/nodes/Manipulator.qml +++ b/tools/animedit/qml/nodes/Manipulator.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/Overlay.qml b/tools/animedit/qml/nodes/Overlay.qml index 96c974d6e55..cb1f1b6990d 100644 --- a/tools/animedit/qml/nodes/Overlay.qml +++ b/tools/animedit/qml/nodes/Overlay.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/PoleVector.qml b/tools/animedit/qml/nodes/PoleVector.qml index bbf5e96a2fa..050cea6ccb9 100644 --- a/tools/animedit/qml/nodes/PoleVector.qml +++ b/tools/animedit/qml/nodes/PoleVector.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/RandomStateMachine.qml b/tools/animedit/qml/nodes/RandomStateMachine.qml index 357fc4b2bf8..a01b3158ca3 100644 --- a/tools/animedit/qml/nodes/RandomStateMachine.qml +++ b/tools/animedit/qml/nodes/RandomStateMachine.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/SplineIK.qml b/tools/animedit/qml/nodes/SplineIK.qml index df1c8931a3f..ceb9ead9297 100644 --- a/tools/animedit/qml/nodes/SplineIK.qml +++ b/tools/animedit/qml/nodes/SplineIK.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/StateMachine.qml b/tools/animedit/qml/nodes/StateMachine.qml index aec4c30d272..8a3ec4de103 100644 --- a/tools/animedit/qml/nodes/StateMachine.qml +++ b/tools/animedit/qml/nodes/StateMachine.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/animedit/qml/nodes/TwoBoneIK.qml b/tools/animedit/qml/nodes/TwoBoneIK.qml index 6bdae5c1bed..4ea6445861f 100644 --- a/tools/animedit/qml/nodes/TwoBoneIK.qml +++ b/tools/animedit/qml/nodes/TwoBoneIK.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.6 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.0 +import QtQuick.Dialogs import "../fields" Column { diff --git a/tools/conan-profiles/linux b/tools/conan-profiles/linux index 63f9be56441..eb4265d8db4 100644 --- a/tools/conan-profiles/linux +++ b/tools/conan-profiles/linux @@ -36,3 +36,5 @@ nss*:shared=True nspr*:shared=True # Work around `undefined reference` errors when building NSS. sqlite3*:shared=True +# Required by Qt6 WebEngine. +qt*:with_dbus=True diff --git a/tools/conan-profiles/vs-22-release b/tools/conan-profiles/vs-22-release index f48eac071ed..b2fbe93f70a 100644 --- a/tools/conan-profiles/vs-22-release +++ b/tools/conan-profiles/vs-22-release @@ -12,3 +12,5 @@ build_type=Release Overte/*:qt_source=source # Required for building oneTBB. hwloc/*:shared=True +# libpq throws linker errors on CI and not needed (PostgreSQL). +qt*:with_pq=False diff --git a/tools/gpu-frame-player/src/RenderThread.cpp b/tools/gpu-frame-player/src/RenderThread.cpp index 5240ae6ea75..aa46245e6e6 100644 --- a/tools/gpu-frame-player/src/RenderThread.cpp +++ b/tools/gpu-frame-player/src/RenderThread.cpp @@ -11,7 +11,7 @@ #include #include #ifdef Q_OS_LINUX -#include +//#include #endif #ifdef USE_GL #include diff --git a/tools/nitpick/CMakeLists.txt b/tools/nitpick/CMakeLists.txt index 3b4f3917201..0b3179a6889 100644 --- a/tools/nitpick/CMakeLists.txt +++ b/tools/nitpick/CMakeLists.txt @@ -4,13 +4,13 @@ project(${TARGET_NAME}) set(CUSTOM_NITPICK_QRC_PATHS "") find_npm() -find_package(Qt5 COMPONENTS Widgets) +find_package(Qt6 COMPONENTS Widgets) set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) set(RESOURCES_RCC ${CMAKE_CURRENT_BINARY_DIR}/resources.rcc) generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources CUSTOM_PATHS ${CUSTOM_NITPICK_QRC_PATHS} GLOBS *) -qt5_add_binary_resources(nitpick_resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}" OPTIONS -no-compress) +qt6_add_binary_resources(nitpick_resources "${RESOURCES_QRC}" DESTINATION "${RESOURCES_RCC}" OPTIONS -no-compress) # grab the implementation and header files from src dirs file(GLOB_RECURSE NITPICK_SRCS "src/*.cpp" "src/*.h") @@ -23,7 +23,7 @@ file (GLOB_RECURSE QT_UI_FILES ui/*.ui) source_group("UI Files" FILES ${QT_UI_FILES}) # have qt5 wrap them and generate the appropriate header files -qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") +qt6_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") setup_memory_debugger() setup_thread_debugger() diff --git a/tools/nitpick/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp index 894dfa85f7b..76aee17cde6 100644 --- a/tools/nitpick/src/AWSInterface.cpp +++ b/tools/nitpick/src/AWSInterface.cpp @@ -43,7 +43,7 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, QStringList parts = testResults.split('/'); QString zipFilename = parts[parts.length() - 1]; - QStringList zipFolderNameParts = zipFilename.split(QRegExp("[\\(\\)\\[\\]]"), Qt::SkipEmptyParts); + QStringList zipFolderNameParts = zipFilename.split(QRegularExpression("[\\(\\)\\[\\]]"), Qt::SkipEmptyParts); if (!QRegularExpression("TestResults--\\d{4}(-\\d\\d){2}_\\d\\d(-\\d\\d){2}").match(zipFolderNameParts[0]).hasMatch() || !QRegularExpression("\\w").match(zipFolderNameParts[1]).hasMatch() || // build (local, build number or PR number) diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index 0ae3e08353c..7fbcf6913bb 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -1051,7 +1051,7 @@ void TestCreator::createTestsOutline() { stream << "Directories with an appended (*) have an automatic test\n\n"; // We need to know our current depth, as this isn't given by QDirIterator - int rootDepth { _testsRootDirectory.count('/') }; + qsizetype rootDepth { _testsRootDirectory.count('/') }; // Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file QDirIterator it(_testsRootDirectory, QDirIterator::Subdirectories); @@ -1231,7 +1231,7 @@ QString TestCreator::getExpectedImagePartialSourceDirectory(const QString& filen // Note that the bottom-most "tests" folder is assumed to be the root // This is required because the tests folder is named hifi_tests - int i { filenameParts.length() - 1 }; + qsizetype i { filenameParts.length() - 1 }; while (i >= 0 && filenameParts[i] != "tests") { --i; } diff --git a/tools/nitpick/src/TestRunnerMobile.cpp b/tools/nitpick/src/TestRunnerMobile.cpp index 53a74da82f5..a9a52b5bfa7 100644 --- a/tools/nitpick/src/TestRunnerMobile.cpp +++ b/tools/nitpick/src/TestRunnerMobile.cpp @@ -13,6 +13,9 @@ #include #include #include +#if defined Q_OS_WIN || defined Q_OS_MAC +#include +#endif #include "Nitpick.h" extern Nitpick* nitpick; @@ -104,15 +107,15 @@ void TestRunnerMobile::connectDevice() { if (line2.contains("unauthorized")) { QMessageBox::critical(0, "Unauthorized device detected", "Please allow USB debugging on device"); } else if (line2.contains(DEVICE)) { - // Make sure only 1 device + // Make sure only 1 device QString line3 = devicesFile.readLine(); if (line3.contains(DEVICE)) { QMessageBox::critical(0, "Too many devices detected", "Tests will run only if a single device is attached"); } else { // Line looks like this: 988a1b47335239434b device product:dream2qlteue model:SM_G955U1 device:dream2qlteue transport_id:2 - QStringList tokens = line2.split(QRegExp("[\r\n\t ]+")); + QStringList tokens = line2.split(QRegularExpression("[\r\n\t ]+")); QString deviceID = tokens[0]; - + // Find the model entry _modelName = "UNKNOWN"; for (int i = 0; i < tokens.size(); ++i) { diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 0853b41872c..9f770bea985 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -197,7 +197,7 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i // setup a texture baker for this URL, as long as we aren't baking a texture already if (!_textureBakers.contains(key)) { - auto baseTextureFileName = _textureFileNamer.createBaseTextureFileName(textureURL.fileName(), type); + auto baseTextureFileName = _textureFileNamer.createBaseTextureFileName(QFileInfo(textureURL.fileName()), type); // setup a baker for this texture QSharedPointer textureBaker { diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index a736cf60b80..552e42b7d26 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,7 +54,7 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - HFMModel::Pointer geometry = FBXSerializer().read(blob, QVariantHash()); + HFMModel::Pointer geometry = FBXSerializer().read(blob, hifi::VariantMultiHash()); std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index 54839bbd1c0..290a3d1b3a8 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -14,7 +14,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 +import Qt5Compat.GraphicalEffects import stylesUit 1.0 as HifiStylesUit import controlsUit 1.0 as HifiControlsUit