@@ -158,6 +158,10 @@ if(NOT QT_BUILD_STANDALONE_TESTS)
_qt_internal_create_global_android_targets()
endif()
+ if(OHOS)
+ include(src/corelib/Qt6OpenHarmonyMacros.cmake)
+ endif()
+
if(WASM)
# Needed when building for WebAssembly.
include(cmake/QtWasmHelpers.cmake)
@@ -151,6 +151,92 @@ function(qt_auto_detect_android)
endif()
endfunction()
+# for openharmony
+function(qt_auto_detect_openharmony)
+ if(DEFINED OPENHARMONY_SDK_ROOT
+ OR DEFINED OHOS_ARCH
+ OR OHOS_SDK_VERSION)
+ set(openharmony_detected TRUE)
+ else()
+ set(openharmony_detected FALSE)
+ endif()
+
+ # Auto-detect toolchain file
+ if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED OPENHARMONY_SDK_ROOT)
+ set(toolchain_file "${OPENHARMONY_SDK_ROOT}/native/build/cmake/ohos.toolchain.cmake")
+ if(EXISTS "${toolchain_file}")
+ message(STATUS "OpenHarmony toolchain file within NDK detected: ${toolchain_file}")
+ set(CMAKE_TOOLCHAIN_FILE "${toolchain_file}" CACHE STRING "")
+ else()
+ message(FATAL_ERROR "Cannot find the toolchain file '${toolchain_file}'. "
+ "Please specify the toolchain file with -DCMAKE_TOOLCHAIN_FILE=<file>.")
+ endif()
+ endif()
+
+ if(NOT DEFINED OHOS_SDK_VERSION AND DEFINED OPENHARMONY_SDK_ROOT)
+ set(json_file "${OPENHARMONY_SDK_ROOT}/../sdk-pkg.json")
+ if(EXISTS "${json_file}")
+ file(READ "${json_file}" json_content)
+ string(JSON api_version GET "${json_content}" data apiVersion)
+ if("${api_version}" STREQUAL "")
+ message(FATAL_ERROR "An OpenHarmony build was requested, but no OpenHarmony SDK version was "
+ "specified nor detected.")
+ else()
+ set(OHOS_SDK_VERSION "${api_version}" CACHE STRING "")
+ message(STATUS "OpenHarmony SDK version detected: ${api_version}")
+ endif()
+ else()
+ file(READ "${OPENHARMONY_SDK_ROOT}/native/oh-uni-package.json" json_content)
+ string(JSON api_version GET "${json_content}" apiVersion)
+ if("${api_version}" STREQUAL "")
+ message(FATAL_ERROR "An OpenHarmony build was requested, but no OpenHarmony SDK version was "
+ "specified nor detected.")
+ else()
+ set(OHOS_SDK_VERSION "${api_version}" CACHE STRING "")
+ message(STATUS "OpenHarmony SDK version detected: ${api_version}")
+ endif()
+ endif()
+ if(NOT DEFINED OHOS_SDK_VERSION)
+ message(FATAL_ERROR "An OpenHarmony build was requested, but no OpenHarmony SDK version was "
+ "specified nor detected.")
+ endif()
+ endif()
+
+ if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND openharmony_detected)
+ message(FATAL_ERROR "An OpenHarmony build was requested, but no OpenHarmony toolchain file was "
+ "specified nor detected.")
+ endif()
+
+
+ if(DEFINED CMAKE_TOOLCHAIN_FILE AND NOT DEFINED QT_AUTODETECT_OPENHARMONY)
+ # Peek into the toolchain file and check if it looks like an OpenHarmony one.
+ if(NOT openharmony_detected)
+ file(READ ${CMAKE_TOOLCHAIN_FILE} toolchain_file_content OFFSET 0 LIMIT 80)
+ string(FIND "${toolchain_file_content}" "Huawei Device Co., Ltd"
+ find_result REVERSE)
+ if(NOT ${find_result} EQUAL -1)
+ set(openharmony_detected TRUE)
+ endif()
+ endif()
+
+ if(openharmony_detected)
+ message(STATUS "OpenHarmony build detected, checking configuration defaults...")
+ if(NOT DEFINED OHOS_PLATFORM)
+ set(OHOS_PLATFORM "ohos" CACHE STRING "")
+ endif()
+ if(NOT DEFINED OHOS_STL)
+ set(OHOS_STL "c++_shared" CACHE STRING "")
+ endif()
+ if(DEFINED OHOS_ARCH)
+ set(CMAKE_OPENHARMONY_ARCH_ABI ${OHOS_ARCH} CACHE STRING "")
+ endif()
+ endif()
+ set(QT_AUTODETECT_OPENHARMONY ${openharmony_detected} CACHE STRING "")
+ elseif (QT_AUTODETECT_OPENHARMONY)
+ message(STATUS "OpenHarmony build detected")
+ endif()
+endfunction()
+
function(qt_auto_detect_vcpkg)
if(DEFINED ENV{VCPKG_ROOT})
set(vcpkg_toolchain_file "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
@@ -458,6 +544,7 @@ macro(qt_internal_setup_autodetect)
qt_auto_detect_macos_universal()
qt_auto_detect_ios()
qt_auto_detect_android()
+ qt_auto_detect_openharmony()
qt_auto_detect_vcpkg()
qt_auto_detect_pch()
qt_auto_detect_wasm()
@@ -81,7 +81,7 @@ function(qt_internal_add_executable name)
qt_internal_add_repo_local_defines(${name})
- if(ANDROID)
+ if(ANDROID OR OHOS)
# The above call to qt_set_common_target_properties() sets the symbol
# visibility to hidden, but for Android, we need main() to not be hidden
# because it has to be loadable at runtime using dlopen().
@@ -170,7 +170,7 @@ function(qt_internal_apply_gc_binaries target visibility)
set(gc_sections_flag "-Wl,-dead_strip")
elseif(SOLARIS)
set(gc_sections_flag "-Wl,-z,ignore")
- elseif(LINUX OR BSD OR WIN32 OR ANDROID)
+ elseif(LINUX OR BSD OR WIN32 OR ANDROID OR OHOS)
set(gc_sections_flag "-Wl,--gc-sections")
endif()
@@ -66,6 +66,11 @@ function(qt_internal_set_warnings_are_errors_flags target target_scope)
if (ANDROID)
list(APPEND flags -Wno-error=literal-suffix)
endif()
+
+ if(OHOS)
+ list(APPEND flags -Wno-error=literal-suffix)
+ list(APPEND flags -Wno-error=c++20-extensions)
+ endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Only enable for versions of MSVC that are known to work
# 1939 is Visual Studio 2022 version 17.0
@@ -48,6 +48,8 @@ macro(qt_internal_setup_platform_definitions_and_mkspec)
elseif(CLANG)
set(QT_DEFAULT_MKSPEC linux-clang)
endif()
+ elseif(OHOS)
+ set(QT_DEFAULT_MKSPEC ohos-clang)
elseif(ANDROID)
if(GCC)
set(QT_DEFAULT_MKSPEC android-g++)
@@ -327,13 +327,21 @@ function(qt_internal_add_module target)
endif()
if(NOT arg_HEADER_MODULE)
- set_target_properties(${target} PROPERTIES
- LIBRARY_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
- RUNTIME_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}"
- ARCHIVE_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
- VERSION ${PROJECT_VERSION}
- SOVERSION ${PROJECT_VERSION_MAJOR}
- )
+ if(OHOS)
+ set_target_properties(${target} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
+ RUNTIME_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}"
+ ARCHIVE_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
+ )
+ else()
+ set_target_properties(${target} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
+ RUNTIME_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}"
+ ARCHIVE_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ )
+ endif()
qt_set_target_info_properties(${target} ${ARGN})
qt_handle_multi_config_output_dirs("${target}")
@@ -11,6 +11,7 @@ endfunction()
qt_set01(LINUX CMAKE_SYSTEM_NAME STREQUAL "Linux")
qt_set01(HPUX CMAKE_SYSTEM_NAME STREQUAL "HPUX")
+qt_set01(OHOS CMAKE_SYSTEM_NAME STREQUAL "OHOS")
qt_set01(ANDROID CMAKE_SYSTEM_NAME STREQUAL "Android") # FIXME: How to identify this?
qt_set01(INTEGRITY CMAKE_SYSTEM_NAME STREQUAL "Integrity") # FIXME: How to identify this?
qt_set01(VXWORKS CMAKE_SYSTEM_NAME STREQUAL "VxWorks") # FIXME: How to identify this?
@@ -135,7 +135,11 @@ function(qt_internal_add_plugin target)
# Make sure the Qt6 plugin library names are like they were in Qt5 qmake land.
# Whereas the Qt6 CMake target names are like the Qt5 CMake target names.
get_target_property(output_name ${target} OUTPUT_NAME)
- set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "${output_name}${QT_LIBINFIX}")
+ if(OHOS)
+ set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "plugins_${plugin_type}_${output_name}${QT_LIBINFIX}")
+ else()
+ set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "${output_name}${QT_LIBINFIX}")
+ endif()
# Add a custom target with the Qt5 qmake name for a more user friendly ninja experience.
if(arg_OUTPUT_NAME AND NOT TARGET "${output_name}")
@@ -792,6 +792,10 @@ QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH}
list(APPEND extra_statements "QT_ARCHS = ${architectures}")
endif()
+ if(OHOS)
+ list(APPEND extra_statements "OHOS_SDK_VERSION = ${OHOS_SDK_VERSION}")
+ endif()
+
list(APPEND extra_statements "QT_EDITION = Open Source")
if(WASM)
@@ -901,6 +901,16 @@ endif()
translate_string_input(android-javac-source QT_ANDROID_JAVAC_SOURCE)
translate_string_input(android-javac-target QT_ANDROID_JAVAC_TARGET)
+#for openharmony
+translate_path_input(openharmony-sdk OPENHARMONY_SDK_ROOT)
+translate_path_input(openharmony-platform OHOS_SDK_VERSION)
+if(DEFINED INPUT_openharmony-abis)
+ if(INPUT_openharmony-abis MATCHES ",")
+ qtConfAddError("The -openharmony-abis option cannot handle more than one ABI "
+ "when building with CMake.")
+ endif()
+ translate_string_input(openharmony-abis OHOS_ARCH)
+endif()
# FIXME: config_help.txt says -sdk should apply to macOS as well.
translate_string_input(sdk QT_UIKIT_SDK)
if(DEFINED INPUT_sdk OR (DEFINED INPUT_xplatform AND INPUT_xplatform STREQUAL "macx-ios-clang")
@@ -77,7 +77,7 @@ endfunction()
#
# QT_DISABLE_RPATH can be set to disable embedding any Qt specific rpaths.
function(qt_apply_rpaths)
- # No rpath support for win32 and android.
+ # No rpath support for win32 and android and openharmony.
if(WIN32 OR ANDROID)
return()
endif()
@@ -101,6 +101,7 @@ function(qt_internal_generate_binary_strip_wrapper)
if((UNIX OR MINGW)
AND NOT APPLE
AND NOT ANDROID
+ AND NOT OHOS
AND CMAKE_STRIP)
# To make reconfiguration more robust when QT_INTERNAL_STRIP_SUPPORTS_KEEP_SECTION is
@@ -93,6 +93,10 @@ The following table describes the mapping of configure options to CMake argument
| -sdk <sdk> | -DQT_UIKIT_SDK=<value> | Should be provided a value like 'iphoneos' or 'iphonesimulator' |
| | | If no value is provided, a simulator_and_device build is |
| | | assumed. |
+| -openharmony-sdk <path> | -DOPENHARMONY_SDK_ROOT=<path> |
+| -openharmony-abis <abi_1>,...,<abi_n> | -DOHOS_ARCH=<abi_1> |
+| -openharmony-platform 17 | -DOHOS_SDK_VERSION=17 |
+| | |
| -android-sdk <path> | -DANDROID_SDK_ROOT=<path> | |
| -android-ndk <path> | -DCMAKE_TOOLCHAIN_FILE=<toolchain file in NDK> | |
| -android-ndk-platform android-23 | -DANDROID_PLATFORM=android-23 | |
@@ -174,6 +174,10 @@ Build environment:
should be one of the available SDKs as listed by
'xcodebuild -showsdks'.
+ -openharmony-sdk path .... Set OpenHarmony SDK root path [$OPENHARMONY_SDK_ROOT]
+ -openharmony-abis ....... Only one ABI can be specified, default is: arm64-v8a
+ -openharmony-platform .... Set OpenHarmony SDK version [$OHOS_SDK_VERSION]
+
-android-sdk path .... Set Android SDK root path [$ANDROID_SDK_ROOT]
-android-ndk path .... Set Android NDK root path [$ANDROID_NDK_ROOT]
-android-ndk-platform Set Android platform
@@ -1354,6 +1354,11 @@ qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC)
qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC)
qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)
+if(OHOS)
+ qt_extra_definition("OHOS_SDK_VERSION" ${OHOS_SDK_VERSION} PUBLIC)
+ qt_extra_definition("NAPI_DISABLE_CPP_EXCEPTIONS" "1" PUBLIC)
+endif()
+
qt_extra_definition("QT_COPYRIGHT" \"${QT_COPYRIGHT}\" PRIVATE)
qt_extra_definition("QT_COPYRIGHT_YEAR" \"${QT_COPYRIGHT_YEAR}\" PRIVATE)
new file mode 100644
@@ -0,0 +1,87 @@
+# qmake configuration for building with ohos-clang
+MAKEFILE_GENERATOR = UNIX
+QMAKE_PLATFORM = openharmony
+QMAKE_COMPILER = clang
+
+CONFIG += unversioned_soname unversioned_libname plugin_with_soname android_deployment_settings
+
+include(../common/linux.conf)
+include(../common/gcc-base-unix.conf)
+include(../common/clang.conf)
+
+load(device_config)
+
+NDK_ROOT = $$(OPENHARMONY_NDK_ROOT)
+!exists($$NDK_ROOT): error("You need to set the OPENHARMONY_NDK_ROOT environment variable to point to your OpenHarmony NDK.")
+MESSAGE($${NDK_ROOT})
+ALL_OPENHARMONY_ABIS = $$(ALL_OPENHARMONY_ABIS)
+isEmpty(ALL_OPENHARMONY_ABIS): ALL_OPENHARMONY_ABIS = $$DEFAULT_OPENHARMONY_ABIS
+isEmpty(ALL_OPENHARMONY_ABIS): ALL_OPENHARMONY_ABIS = arm64-v8a armeabi-v7a x86_64 x86
+
+NDK_LLVM_PATH = $$NDK_ROOT/native/llvm
+QMAKE_CC = $$NDK_LLVM_PATH/bin/clang
+QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++
+QMAKE_LINK = $$QMAKE_CXX
+QMAKE_OBJCOPY = $$NDK_LLVM_PATH/bin/llvm-objcopy
+QMAKE_AR = $$NDK_LLVM_PATH/bin/llvm-ar cqs
+QMAKE_OBJCOPY = $$NDK_LLVM_PATH/bin/llvm-objcopy
+QMAKE_NM = $$NDK_LLVM_PATH/bin/llvm-nm -P
+
+QMAKE_CFLAGS_OPTIMIZE = -Oz
+QMAKE_CFLAGS_OPTIMIZE_FULL = -Oz
+
+QMAKE_CFLAGS_WARN_ON = -Wall -W
+QMAKE_CFLAGS_WARN_OFF =
+QMAKE_CFLAGS_SHLIB = -fPIC
+QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses
+QMAKE_CFLAGS_THREAD = -D_REENTRANT
+QMAKE_CFLAGS_HIDESYMS = -fvisibility=hidden
+QMAKE_CFLAGS_NEON = -mfpu=neon
+
+QMAKE_LFLAGS_APP = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
+QMAKE_LFLAGS_SHLIB = -Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack -shared
+QMAKE_LFLAGS_PLUGIN = $$QMAKE_LFLAGS_SHLIB
+QMAKE_LFLAGS_NOUNDEF = -Wl,--no-undefined
+QMAKE_LFLAGS_RPATH = -Wl,-rpath=
+QMAKE_LFLAGS_RPATHLINK = -Wl,-rpath-link=
+
+equals(QMAKE_HOST.os, Windows) {
+ QMAKE_LINK_OBJECT_MAX = 10
+}
+
+QMAKE_LIBS_X11 =
+QMAKE_LIBS_THREAD =
+QMAKE_LIBS_OPENGL =
+QMAKE_INCDIR_POST =
+QMAKE_INCDIR_X11 =
+QMAKE_LIBDIR_X11 =
+QMAKE_INCDIR_OPENGL =
+QMAKE_LIBDIR_OPENGL =
+
+OPENHARMONY_USE_LLVM = true
+
+armeabi-v7a.sdk = armeabi-v7a
+armeabi-v7a.target = armeabi-v7a
+armeabi-v7a.dir_affix = armeabi-v7a
+armeabi-v7a.CONFIG = armeabi-v7a
+armeabi-v7a.deployment_identifier = armeabi-v7a
+
+arm64-v8a.sdk = arm64-v8a
+arm64-v8a.target = arm64-v8a
+arm64-v8a.dir_affix = arm64-v8a
+arm64-v8a.CONFIG = arm64-v8a
+arm64-v8a.deployment_identifier = arm64-v8a
+
+x86.sdk = x86
+x86.target = x86
+x86.dir_affix = x86
+x86.CONFIG = x86
+x86.deployment_identifier = x86
+
+x86_64.sdk = x86_64
+x86_64.target = x86_64
+x86_64.dir_affix = x86_64
+x86_64.CONFIG = x86_64
+x86_64.deployment_identifier = x86_64
+
+load(qt_config)
new file mode 100644
@@ -0,0 +1,141 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMDEFS_H
+#define QPLATFORMDEFS_H
+
+// Get Qt defines/settings
+
+#include "qglobal.h"
+
+// Set any POSIX/XOPEN defines at the top of this file to turn on specific APIs
+
+// 1) need to reset default environment if _BSD_SOURCE is defined
+// 2) need to specify POSIX thread interfaces explicitly in glibc 2.0
+// 3) it seems older glibc need this to include the X/Open stuff
+
+#include <unistd.h>
+
+// We are hot - unistd.h should have turned on the specific APIs we requested
+
+#include <features.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/ipc.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_STATBUF struct stat64
+#define QT_STATBUF4TSTAT struct stat64
+#define QT_STAT ::stat64
+#define QT_FSTAT ::fstat64
+#define QT_LSTAT ::lstat64
+#define QT_OPEN ::open64
+#define QT_TRUNCATE ::truncate64
+#define QT_FTRUNCATE ::ftruncate64
+#define QT_LSEEK ::lseek64
+#else
+#define QT_STATBUF struct stat
+#define QT_STATBUF4TSTAT struct stat
+#define QT_STAT ::stat
+#define QT_FSTAT ::fstat
+#define QT_LSTAT ::lstat
+#define QT_OPEN ::open
+#define QT_TRUNCATE ::truncate
+#define QT_FTRUNCATE ::ftruncate
+#define QT_LSEEK ::lseek
+#endif
+
+#ifdef QT_LARGEFILE_SUPPORT
+#define QT_FOPEN ::fopen64
+#define QT_FSEEK ::fseeko64
+#define QT_FTELL ::ftello64
+#define QT_FGETPOS ::fgetpos64
+#define QT_FSETPOS ::fsetpos64
+#define QT_MMAP ::mmap64
+#define QT_FPOS_T fpos64_t
+#define QT_OFF_T off64_t
+#else
+#define QT_FOPEN ::fopen
+#define QT_FSEEK ::fseek
+#define QT_FTELL ::ftell
+#define QT_FGETPOS ::fgetpos
+#define QT_FSETPOS ::fsetpos
+#define QT_MMAP ::mmap
+#define QT_FPOS_T fpos_t
+#define QT_OFF_T long
+#endif
+
+#define QT_STAT_REG S_IFREG
+#define QT_STAT_DIR S_IFDIR
+#define QT_STAT_MASK S_IFMT
+#define QT_STAT_LNK S_IFLNK
+#define QT_SOCKET_CONNECT ::connect
+#define QT_SOCKET_BIND ::bind
+#define QT_FILENO fileno
+#define QT_CLOSE ::close
+#define QT_READ ::read
+#define QT_WRITE ::write
+#define QT_ACCESS ::access
+#define QT_GETCWD ::getcwd
+#define QT_CHDIR ::chdir
+#define QT_MKDIR ::mkdir
+#define QT_RMDIR ::rmdir
+#define QT_OPEN_LARGEFILE O_LARGEFILE
+#define QT_OPEN_RDONLY O_RDONLY
+#define QT_OPEN_WRONLY O_WRONLY
+#define QT_OPEN_RDWR O_RDWR
+#define QT_OPEN_CREAT O_CREAT
+#define QT_OPEN_TRUNC O_TRUNC
+#define QT_OPEN_APPEND O_APPEND
+#define QT_OPEN_EXCL O_EXCL
+
+// Directory iteration
+#define QT_DIR DIR
+
+#define QT_OPENDIR ::opendir
+#define QT_CLOSEDIR ::closedir
+
+#if defined(QT_LARGEFILE_SUPPORT) \
+ && defined(QT_USE_XOPEN_LFS_EXTENSIONS) \
+ && !defined(QT_NO_READDIR64)
+#define QT_DIRENT struct dirent64
+#define QT_READDIR ::readdir64
+#define QT_READDIR_R ::readdir64_r
+#else
+#define QT_DIRENT struct dirent
+#define QT_READDIR ::readdir
+#define QT_READDIR_R ::readdir_r
+#endif
+
+#define QT_SOCKET_CONNECT ::connect
+#define QT_SOCKET_BIND ::bind
+
+
+#define QT_SIGNAL_RETTYPE void
+#define QT_SIGNAL_ARGS int
+#define QT_SIGNAL_IGNORE SIG_IGN
+
+#define QT_SOCKLEN_T socklen_t
+
+#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)
+#define QT_SNPRINTF ::snprintf
+#define QT_VSNPRINTF ::vsnprintf
+#endif
+
+#endif // QPLATFORMDEFS_H
@@ -30,6 +30,9 @@ qt_commandline_option(settingsdir TYPE string NAME sysconfdir)
qt_commandline_option(sysconfdir TYPE string)
qt_commandline_option(testsdir TYPE string)
qt_commandline_option(translationdir TYPE string)
+qt_commandline_option(openharmony-sdk TYPE string)
+qt_commandline_option(openharmony-platform TYPE string)
+qt_commandline_option(openharmony-abis TYPE string)
qt_commandline_option(android-arch TYPE string)
qt_commandline_option(android-abis TYPE string)
qt_commandline_option(android-ndk TYPE string)
new file mode 100644
@@ -0,0 +1,17 @@
+qt_internal_add_3rdparty_library(Napi
+ INTERFACE
+ SOURCES
+ napi.h
+ napi-inl.h
+ napi-inl-deprecated.h
+
+)
+
+qt_internal_add_sync_header_dependencies(Napi Core)
+
+qt_internal_add_3rdparty_header_module(NapiPrivate
+ EXTERNAL_HEADERS
+ napi.h
+ napi-inl.h
+ napi-inl-deprecated.h
+)
new file mode 100644
@@ -0,0 +1,192 @@
+#ifndef SRC_NAPI_INL_DEPRECATED_H_
+#define SRC_NAPI_INL_DEPRECATED_H_
+
+////////////////////////////////////////////////////////////////////////////////
+// PropertyDescriptor class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename Getter>
+inline PropertyDescriptor
+PropertyDescriptor::Accessor(const char* utf8name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef details::CallbackData<Getter, Napi::Value> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ getter, nullptr });
+
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ nullptr,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* data) {
+ return Accessor(utf8name.c_str(), getter, attributes, data);
+}
+
+template <typename Getter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef details::CallbackData<Getter, Napi::Value> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ getter, nullptr });
+
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ nullptr,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Name name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* data) {
+ napi_value nameValue = name;
+ return PropertyDescriptor::Accessor(nameValue, getter, attributes, data);
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef details::AccessorCallbackData<Getter, Setter> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ getter, setter, nullptr });
+
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ nullptr,
+ CbData::GetterWrapper,
+ CbData::SetterWrapper,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* data) {
+ return Accessor(utf8name.c_str(), getter, setter, attributes, data);
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef details::AccessorCallbackData<Getter, Setter> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ getter, setter, nullptr });
+
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ nullptr,
+ CbData::GetterWrapper,
+ CbData::SetterWrapper,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Name name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* data) {
+ napi_value nameValue = name;
+ return PropertyDescriptor::Accessor(nameValue, getter, setter, attributes, data);
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(const char* utf8name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType;
+ typedef details::CallbackData<Callable, ReturnType> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ cb, nullptr });
+
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(const std::string& utf8name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* data) {
+ return Function(utf8name.c_str(), cb, attributes, data);
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(napi_value name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* /*data*/) {
+ typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType;
+ typedef details::CallbackData<Callable, ReturnType> CbData;
+ // TODO: Delete when the function is destroyed
+ auto callbackData = new CbData({ cb, nullptr });
+
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(Name name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* data) {
+ napi_value nameValue = name;
+ return PropertyDescriptor::Function(nameValue, cb, attributes, data);
+}
+
+#endif // !SRC_NAPI_INL_DEPRECATED_H_
new file mode 100644
@@ -0,0 +1,4097 @@
+#ifndef SRC_NAPI_INL_H_
+#define SRC_NAPI_INL_H_
+
+////////////////////////////////////////////////////////////////////////////////
+// N-API C++ Wrapper Classes
+//
+// Inline header-only implementations for "N-API" ABI-stable C APIs for Node.js.
+////////////////////////////////////////////////////////////////////////////////
+
+// Note: Do not include this file directly! Include "napi.h" instead.
+
+#include <cstring>
+#include <type_traits>
+
+namespace Napi {
+
+// Helpers to handle functions exposed from C++.
+namespace details {
+
+// Attach a data item to an object and delete it when the object gets
+// garbage-collected.
+// TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
+// available on all supported versions of Node.js.
+template <typename FreeType>
+static inline napi_status AttachData(napi_env env,
+ napi_value obj,
+ FreeType* data) {
+ napi_value symbol, external;
+ napi_status status = napi_create_symbol(env, nullptr, &symbol);
+ if (status == napi_ok) {
+ status = napi_create_external(env,
+ data,
+ [](napi_env /*env*/, void* data, void* /*hint*/) {
+ delete static_cast<FreeType*>(data);
+ },
+ nullptr,
+ &external);
+ if (status == napi_ok) {
+ napi_property_descriptor desc = {
+ nullptr,
+ symbol,
+ nullptr,
+ nullptr,
+ nullptr,
+ external,
+ napi_default,
+ nullptr
+ };
+ status = napi_define_properties(env, obj, 1, &desc);
+ }
+ }
+ return status;
+}
+
+// For use in JS to C++ callback wrappers to catch any Napi::Error exceptions
+// and rethrow them as JavaScript exceptions before returning from the callback.
+template <typename Callable>
+inline napi_value WrapCallback(Callable callback) {
+#ifdef NAPI_CPP_EXCEPTIONS
+ try {
+ return callback();
+ } catch (const Error& e) {
+ e.ThrowAsJavaScriptException();
+ return nullptr;
+ }
+#else // NAPI_CPP_EXCEPTIONS
+ // When C++ exceptions are disabled, errors are immediately thrown as JS
+ // exceptions, so there is no need to catch and rethrow them here.
+ return callback();
+#endif // NAPI_CPP_EXCEPTIONS
+}
+
+template <typename Callable, typename Return>
+struct CallbackData {
+ static inline
+ napi_value Wrapper(napi_env env, napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ CallbackData* callbackData =
+ static_cast<CallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ return callbackData->callback(callbackInfo);
+ });
+ }
+
+ Callable callback;
+ void* data;
+};
+
+template <typename Callable>
+struct CallbackData<Callable, void> {
+ static inline
+ napi_value Wrapper(napi_env env, napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ CallbackData* callbackData =
+ static_cast<CallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ callbackData->callback(callbackInfo);
+ return nullptr;
+ });
+ }
+
+ Callable callback;
+ void* data;
+};
+
+template <typename T, typename Finalizer, typename Hint = void>
+struct FinalizeData {
+ static inline
+ void Wrapper(napi_env env, void* data, void* finalizeHint) {
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
+ finalizeData->callback(Env(env), static_cast<T*>(data));
+ delete finalizeData;
+ }
+
+ static inline
+ void WrapperWithHint(napi_env env, void* data, void* finalizeHint) {
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
+ finalizeData->callback(Env(env), static_cast<T*>(data), finalizeData->hint);
+ delete finalizeData;
+ }
+
+ Finalizer callback;
+ Hint* hint;
+};
+
+#if (NAPI_VERSION > 3)
+template <typename ContextType=void,
+ typename Finalizer=std::function<void(Env, void*, ContextType*)>,
+ typename FinalizerDataType=void>
+struct ThreadSafeFinalize {
+ static inline
+ void Wrapper(napi_env env, void* rawFinalizeData, void* /* rawContext */) {
+ if (rawFinalizeData == nullptr)
+ return;
+
+ ThreadSafeFinalize* finalizeData =
+ static_cast<ThreadSafeFinalize*>(rawFinalizeData);
+ finalizeData->callback(Env(env));
+ if (finalizeData->tsfn) {
+ *finalizeData->tsfn = nullptr;
+ }
+ delete finalizeData;
+ }
+
+ static inline
+ void FinalizeWrapperWithData(napi_env env,
+ void* rawFinalizeData,
+ void* /* rawContext */) {
+ if (rawFinalizeData == nullptr)
+ return;
+
+ ThreadSafeFinalize* finalizeData =
+ static_cast<ThreadSafeFinalize*>(rawFinalizeData);
+ finalizeData->callback(Env(env), finalizeData->data);
+ if (finalizeData->tsfn) {
+ *finalizeData->tsfn = nullptr;
+ }
+ delete finalizeData;
+ }
+
+ static inline
+ void FinalizeWrapperWithContext(napi_env env,
+ void* rawFinalizeData,
+ void* rawContext) {
+ if (rawFinalizeData == nullptr)
+ return;
+
+ ThreadSafeFinalize* finalizeData =
+ static_cast<ThreadSafeFinalize*>(rawFinalizeData);
+ finalizeData->callback(Env(env), static_cast<ContextType*>(rawContext));
+ if (finalizeData->tsfn) {
+ *finalizeData->tsfn = nullptr;
+ }
+ delete finalizeData;
+ }
+
+ static inline
+ void FinalizeFinalizeWrapperWithDataAndContext(napi_env env,
+ void* rawFinalizeData,
+ void* rawContext) {
+ if (rawFinalizeData == nullptr)
+ return;
+
+ ThreadSafeFinalize* finalizeData =
+ static_cast<ThreadSafeFinalize*>(rawFinalizeData);
+ finalizeData->callback(Env(env), finalizeData->data,
+ static_cast<ContextType*>(rawContext));
+ if (finalizeData->tsfn) {
+ *finalizeData->tsfn = nullptr;
+ }
+ delete finalizeData;
+ }
+
+ FinalizerDataType* data;
+ Finalizer callback;
+ napi_threadsafe_function* tsfn;
+};
+#endif
+
+template <typename Getter, typename Setter>
+struct AccessorCallbackData {
+ static inline
+ napi_value GetterWrapper(napi_env env, napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ AccessorCallbackData* callbackData =
+ static_cast<AccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ return callbackData->getterCallback(callbackInfo);
+ });
+ }
+
+ static inline
+ napi_value SetterWrapper(napi_env env, napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ AccessorCallbackData* callbackData =
+ static_cast<AccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ callbackData->setterCallback(callbackInfo);
+ return nullptr;
+ });
+ }
+
+ Getter getterCallback;
+ Setter setterCallback;
+ void* data;
+};
+
+} // namespace details
+
+#ifndef NODE_ADDON_API_DISABLE_DEPRECATED
+# include "napi-inl-deprecated.h"
+#endif // !NODE_ADDON_API_DISABLE_DEPRECATED
+
+////////////////////////////////////////////////////////////////////////////////
+// Module registration
+////////////////////////////////////////////////////////////////////////////////
+
+#define NODE_API_MODULE(modname, regfunc) \
+ napi_value __napi_ ## regfunc(napi_env env, \
+ napi_value exports) { \
+ return Napi::RegisterModule(env, exports, regfunc); \
+ } \
+ NAPI_MODULE(modname, __napi_ ## regfunc)
+
+// Adapt the NAPI_MODULE registration function:
+// - Wrap the arguments in NAPI wrappers.
+// - Catch any NAPI errors and rethrow as JS exceptions.
+inline napi_value RegisterModule(napi_env env,
+ napi_value exports,
+ ModuleRegisterCallback registerCallback) {
+ return details::WrapCallback([&] {
+ return napi_value(registerCallback(Napi::Env(env),
+ Napi::Object(env, exports)));
+ });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Env class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Env::Env(napi_env env) : _env(env) {
+}
+
+inline Env::operator napi_env() const {
+ return _env;
+}
+
+inline Object Env::Global() const {
+ napi_value value;
+ napi_status status = napi_get_global(*this, &value);
+ NAPI_THROW_IF_FAILED(*this, status, Object());
+ return Object(*this, value);
+}
+
+inline Value Env::Undefined() const {
+ napi_value value;
+ napi_status status = napi_get_undefined(*this, &value);
+ NAPI_THROW_IF_FAILED(*this, status, Value());
+ return Value(*this, value);
+}
+
+inline Value Env::Null() const {
+ napi_value value;
+ napi_status status = napi_get_null(*this, &value);
+ NAPI_THROW_IF_FAILED(*this, status, Value());
+ return Value(*this, value);
+}
+
+inline bool Env::IsExceptionPending() const {
+ bool result;
+ napi_status status = napi_is_exception_pending(_env, &result);
+ if (status != napi_ok) result = false; // Checking for a pending exception shouldn't throw.
+ return result;
+}
+
+inline Error Env::GetAndClearPendingException() {
+ napi_value value;
+ napi_status status = napi_get_and_clear_last_exception(_env, &value);
+ if (status != napi_ok) {
+ // Don't throw another exception when failing to get the exception!
+ return Error();
+ }
+ return Error(_env, value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Value class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Value::Value() : _env(nullptr), _value(nullptr) {
+}
+
+inline Value::Value(napi_env env, napi_value value) : _env(env), _value(value) {
+}
+
+inline Value::operator napi_value() const {
+ return _value;
+}
+
+inline bool Value::operator ==(const Value& other) const {
+ return StrictEquals(other);
+}
+
+inline bool Value::operator !=(const Value& other) const {
+ return !this->operator ==(other);
+}
+
+inline bool Value::StrictEquals(const Value& other) const {
+ bool result;
+ napi_status status = napi_strict_equals(_env, *this, other, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline Napi::Env Value::Env() const {
+ return Napi::Env(_env);
+}
+
+inline bool Value::IsEmpty() const {
+ return _value == nullptr;
+}
+
+inline napi_valuetype Value::Type() const {
+ if (IsEmpty()) {
+ return napi_undefined;
+ }
+
+ napi_valuetype type;
+ napi_status status = napi_typeof(_env, _value, &type);
+ NAPI_THROW_IF_FAILED(_env, status, napi_undefined);
+ return type;
+}
+
+inline bool Value::IsUndefined() const {
+ return Type() == napi_undefined;
+}
+
+inline bool Value::IsNull() const {
+ return Type() == napi_null;
+}
+
+inline bool Value::IsBoolean() const {
+ return Type() == napi_boolean;
+}
+
+inline bool Value::IsNumber() const {
+ return Type() == napi_number;
+}
+
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+inline bool Value::IsBigInt() const {
+ return Type() == napi_bigint;
+}
+#endif // NAPI_EXPERIMENTAL
+
+inline bool Value::IsString() const {
+ return Type() == napi_string;
+}
+
+inline bool Value::IsSymbol() const {
+ return Type() == napi_symbol;
+}
+
+inline bool Value::IsArray() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_array(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsArrayBuffer() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_arraybuffer(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsTypedArray() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_typedarray(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsObject() const {
+ return Type() == napi_object || IsFunction();
+}
+
+inline bool Value::IsFunction() const {
+ return Type() == napi_function;
+}
+
+inline bool Value::IsPromise() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_promise(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsDataView() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_dataview(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsBuffer() const {
+ if (IsEmpty()) {
+ return false;
+ }
+
+ bool result;
+ napi_status status = napi_is_buffer(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Value::IsExternal() const {
+ return Type() == napi_external;
+}
+
+template <typename T>
+inline T Value::As() const {
+ return T(_env, _value);
+}
+
+inline Boolean Value::ToBoolean() const {
+ napi_value result;
+ napi_status status = napi_coerce_to_bool(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Boolean());
+ return Boolean(_env, result);
+}
+
+inline Number Value::ToNumber() const {
+ napi_value result;
+ napi_status status = napi_coerce_to_number(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Number());
+ return Number(_env, result);
+}
+
+inline String Value::ToString() const {
+ napi_value result;
+ napi_status status = napi_coerce_to_string(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, String());
+ return String(_env, result);
+}
+
+inline Object Value::ToObject() const {
+ napi_value result;
+ napi_status status = napi_coerce_to_object(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Object());
+ return Object(_env, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Boolean class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Boolean Boolean::New(napi_env env, bool val) {
+ napi_value value;
+ napi_status status = napi_get_boolean(env, val, &value);
+ NAPI_THROW_IF_FAILED(env, status, Boolean());
+ return Boolean(env, value);
+}
+
+inline Boolean::Boolean() : Napi::Value() {
+}
+
+inline Boolean::Boolean(napi_env env, napi_value value) : Napi::Value(env, value) {
+}
+
+inline Boolean::operator bool() const {
+ return Value();
+}
+
+inline bool Boolean::Value() const {
+ bool result;
+ napi_status status = napi_get_value_bool(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Number class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Number Number::New(napi_env env, double val) {
+ napi_value value;
+ napi_status status = napi_create_double(env, val, &value);
+ NAPI_THROW_IF_FAILED(env, status, Number());
+ return Number(env, value);
+}
+
+inline Number::Number() : Value() {
+}
+
+inline Number::Number(napi_env env, napi_value value) : Value(env, value) {
+}
+
+inline Number::operator int32_t() const {
+ return Int32Value();
+}
+
+inline Number::operator uint32_t() const {
+ return Uint32Value();
+}
+
+inline Number::operator int64_t() const {
+ return Int64Value();
+}
+
+inline Number::operator float() const {
+ return FloatValue();
+}
+
+inline Number::operator double() const {
+ return DoubleValue();
+}
+
+inline int32_t Number::Int32Value() const {
+ int32_t result;
+ napi_status status = napi_get_value_int32(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+inline uint32_t Number::Uint32Value() const {
+ uint32_t result;
+ napi_status status = napi_get_value_uint32(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+inline int64_t Number::Int64Value() const {
+ int64_t result;
+ napi_status status = napi_get_value_int64(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+inline float Number::FloatValue() const {
+ return static_cast<float>(DoubleValue());
+}
+
+inline double Number::DoubleValue() const {
+ double result;
+ napi_status status = napi_get_value_double(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+////////////////////////////////////////////////////////////////////////////////
+// BigInt Class
+////////////////////////////////////////////////////////////////////////////////
+
+inline BigInt BigInt::New(napi_env env, int64_t val) {
+ napi_value value;
+ napi_status status = napi_create_bigint_int64(env, val, &value);
+ NAPI_THROW_IF_FAILED(env, status, BigInt());
+ return BigInt(env, value);
+}
+
+inline BigInt BigInt::New(napi_env env, uint64_t val) {
+ napi_value value;
+ napi_status status = napi_create_bigint_uint64(env, val, &value);
+ NAPI_THROW_IF_FAILED(env, status, BigInt());
+ return BigInt(env, value);
+}
+
+inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) {
+ napi_value value;
+ napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value);
+ NAPI_THROW_IF_FAILED(env, status, BigInt());
+ return BigInt(env, value);
+}
+
+inline BigInt::BigInt() : Value() {
+}
+
+inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {
+}
+
+inline int64_t BigInt::Int64Value(bool* lossless) const {
+ int64_t result;
+ napi_status status = napi_get_value_bigint_int64(
+ _env, _value, &result, lossless);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+inline uint64_t BigInt::Uint64Value(bool* lossless) const {
+ uint64_t result;
+ napi_status status = napi_get_value_bigint_uint64(
+ _env, _value, &result, lossless);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+inline size_t BigInt::WordCount() const {
+ size_t word_count;
+ napi_status status = napi_get_value_bigint_words(
+ _env, _value, nullptr, &word_count, nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return word_count;
+}
+
+inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) {
+ napi_status status = napi_get_value_bigint_words(
+ _env, _value, sign_bit, word_count, words);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+#endif // NAPI_EXPERIMENTAL
+
+////////////////////////////////////////////////////////////////////////////////
+// Name class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Name::Name() : Value() {
+}
+
+inline Name::Name(napi_env env, napi_value value) : Value(env, value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// String class
+////////////////////////////////////////////////////////////////////////////////
+
+inline String String::New(napi_env env, const std::string& val) {
+ return String::New(env, val.c_str(), val.size());
+}
+
+inline String String::New(napi_env env, const std::u16string& val) {
+ return String::New(env, val.c_str(), val.size());
+}
+
+inline String String::New(napi_env env, const char* val) {
+ napi_value value;
+ napi_status status = napi_create_string_utf8(env, val, std::strlen(val), &value);
+ NAPI_THROW_IF_FAILED(env, status, String());
+ return String(env, value);
+}
+
+inline String String::New(napi_env env, const char16_t* val) {
+ napi_value value;
+ napi_status status = napi_create_string_utf16(env, val, std::u16string(val).size(), &value);
+ NAPI_THROW_IF_FAILED(env, status, String());
+ return String(env, value);
+}
+
+inline String String::New(napi_env env, const char* val, size_t length) {
+ napi_value value;
+ napi_status status = napi_create_string_utf8(env, val, length, &value);
+ NAPI_THROW_IF_FAILED(env, status, String());
+ return String(env, value);
+}
+
+inline String String::New(napi_env env, const char16_t* val, size_t length) {
+ napi_value value;
+ napi_status status = napi_create_string_utf16(env, val, length, &value);
+ NAPI_THROW_IF_FAILED(env, status, String());
+ return String(env, value);
+}
+
+inline String::String() : Name() {
+}
+
+inline String::String(napi_env env, napi_value value) : Name(env, value) {
+}
+
+inline String::operator std::string() const {
+ return Utf8Value();
+}
+
+inline String::operator std::u16string() const {
+ return Utf16Value();
+}
+
+inline std::string String::Utf8Value() const {
+ size_t length;
+ napi_status status = napi_get_value_string_utf8(_env, _value, nullptr, 0, &length);
+ NAPI_THROW_IF_FAILED(_env, status, "");
+
+ std::string value;
+ value.reserve(length + 1);
+ value.resize(length);
+ status = napi_get_value_string_utf8(_env, _value, &value[0], value.capacity(), nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, "");
+ return value;
+}
+
+inline std::u16string String::Utf16Value() const {
+ size_t length;
+ napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length);
+ NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));
+
+ std::u16string value;
+ value.reserve(length + 1);
+ value.resize(length);
+ status = napi_get_value_string_utf16(_env, _value, &value[0], value.capacity(), nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));
+ return value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Symbol class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Symbol Symbol::New(napi_env env, const char* description) {
+ napi_value descriptionValue = description != nullptr ?
+ String::New(env, description) : static_cast<napi_value>(nullptr);
+ return Symbol::New(env, descriptionValue);
+}
+
+inline Symbol Symbol::New(napi_env env, const std::string& description) {
+ napi_value descriptionValue = String::New(env, description);
+ return Symbol::New(env, descriptionValue);
+}
+
+inline Symbol Symbol::New(napi_env env, String description) {
+ napi_value descriptionValue = description;
+ return Symbol::New(env, descriptionValue);
+}
+
+inline Symbol Symbol::New(napi_env env, napi_value description) {
+ napi_value value;
+ napi_status status = napi_create_symbol(env, description, &value);
+ NAPI_THROW_IF_FAILED(env, status, Symbol());
+ return Symbol(env, value);
+}
+
+inline Symbol Symbol::WellKnown(napi_env env, const std::string& name) {
+ return Napi::Env(env).Global().Get("Symbol").As<Object>().Get(name).As<Symbol>();
+}
+
+inline Symbol::Symbol() : Name() {
+}
+
+inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Automagic value creation
+////////////////////////////////////////////////////////////////////////////////
+
+namespace details {
+template <typename T>
+struct vf_number {
+ static Number From(napi_env env, T value) {
+ return Number::New(env, static_cast<double>(value));
+ }
+};
+
+template<>
+struct vf_number<bool> {
+ static Boolean From(napi_env env, bool value) {
+ return Boolean::New(env, value);
+ }
+};
+
+struct vf_utf8_charp {
+ static String From(napi_env env, const char* value) {
+ return String::New(env, value);
+ }
+};
+
+struct vf_utf16_charp {
+ static String From(napi_env env, const char16_t* value) {
+ return String::New(env, value);
+ }
+};
+struct vf_utf8_string {
+ static String From(napi_env env, const std::string& value) {
+ return String::New(env, value);
+ }
+};
+
+struct vf_utf16_string {
+ static String From(napi_env env, const std::u16string& value) {
+ return String::New(env, value);
+ }
+};
+
+template <typename T>
+struct vf_fallback {
+ static Value From(napi_env env, const T& value) {
+ return Value(env, value);
+ }
+};
+
+template <typename...> struct disjunction : std::false_type {};
+template <typename B> struct disjunction<B> : B {};
+template <typename B, typename... Bs>
+struct disjunction<B, Bs...>
+ : std::conditional<bool(B::value), B, disjunction<Bs...>>::type {};
+
+template <typename T>
+struct can_make_string
+ : disjunction<typename std::is_convertible<T, const char *>::type,
+ typename std::is_convertible<T, const char16_t *>::type,
+ typename std::is_convertible<T, std::string>::type,
+ typename std::is_convertible<T, std::u16string>::type> {};
+}
+
+template <typename T>
+Value Value::From(napi_env env, const T& value) {
+ using Helper = typename std::conditional<
+ std::is_integral<T>::value || std::is_floating_point<T>::value,
+ details::vf_number<T>,
+ typename std::conditional<
+ details::can_make_string<T>::value,
+ String,
+ details::vf_fallback<T>
+ >::type
+ >::type;
+ return Helper::From(env, value);
+}
+
+template <typename T>
+String String::From(napi_env env, const T& value) {
+ struct Dummy {};
+ using Helper = typename std::conditional<
+ std::is_convertible<T, const char*>::value,
+ details::vf_utf8_charp,
+ typename std::conditional<
+ std::is_convertible<T, const char16_t*>::value,
+ details::vf_utf16_charp,
+ typename std::conditional<
+ std::is_convertible<T, std::string>::value,
+ details::vf_utf8_string,
+ typename std::conditional<
+ std::is_convertible<T, std::u16string>::value,
+ details::vf_utf16_string,
+ Dummy
+ >::type
+ >::type
+ >::type
+ >::type;
+ return Helper::From(env, value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Object class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename Key>
+inline Object::PropertyLValue<Key>::operator Value() const {
+ return Object(_env, _object).Get(_key);
+}
+
+template <typename Key> template <typename ValueType>
+inline Object::PropertyLValue<Key>& Object::PropertyLValue<Key>::operator =(ValueType value) {
+ Object(_env, _object).Set(_key, value);
+ return *this;
+}
+
+template <typename Key>
+inline Object::PropertyLValue<Key>::PropertyLValue(Object object, Key key)
+ : _env(object.Env()), _object(object), _key(key) {}
+
+inline Object Object::New(napi_env env) {
+ napi_value value;
+ napi_status status = napi_create_object(env, &value);
+ NAPI_THROW_IF_FAILED(env, status, Object());
+ return Object(env, value);
+}
+
+inline Object::Object() : Value() {
+}
+
+inline Object::Object(napi_env env, napi_value value) : Value(env, value) {
+}
+
+inline Object::PropertyLValue<std::string> Object::operator [](const char* utf8name) {
+ return PropertyLValue<std::string>(*this, utf8name);
+}
+
+inline Object::PropertyLValue<std::string> Object::operator [](const std::string& utf8name) {
+ return PropertyLValue<std::string>(*this, utf8name);
+}
+
+inline Object::PropertyLValue<uint32_t> Object::operator [](uint32_t index) {
+ return PropertyLValue<uint32_t>(*this, index);
+}
+
+inline Value Object::operator [](const char* utf8name) const {
+ return Get(utf8name);
+}
+
+inline Value Object::operator [](const std::string& utf8name) const {
+ return Get(utf8name);
+}
+
+inline Value Object::operator [](uint32_t index) const {
+ return Get(index);
+}
+
+inline bool Object::Has(napi_value key) const {
+ bool result;
+ napi_status status = napi_has_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::Has(Value key) const {
+ bool result;
+ napi_status status = napi_has_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::Has(const char* utf8name) const {
+ bool result;
+ napi_status status = napi_has_named_property(_env, _value, utf8name, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::Has(const std::string& utf8name) const {
+ return Has(utf8name.c_str());
+}
+
+inline bool Object::HasOwnProperty(napi_value key) const {
+ bool result;
+ napi_status status = napi_has_own_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::HasOwnProperty(Value key) const {
+ bool result;
+ napi_status status = napi_has_own_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::HasOwnProperty(const char* utf8name) const {
+ napi_value key;
+ napi_status status = napi_create_string_utf8(_env, utf8name, std::strlen(utf8name), &key);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return HasOwnProperty(key);
+}
+
+inline bool Object::HasOwnProperty(const std::string& utf8name) const {
+ return HasOwnProperty(utf8name.c_str());
+}
+
+inline Value Object::Get(napi_value key) const {
+ napi_value result;
+ napi_status status = napi_get_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+inline Value Object::Get(Value key) const {
+ napi_value result;
+ napi_status status = napi_get_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+inline Value Object::Get(const char* utf8name) const {
+ napi_value result;
+ napi_status status = napi_get_named_property(_env, _value, utf8name, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+inline Value Object::Get(const std::string& utf8name) const {
+ return Get(utf8name.c_str());
+}
+
+template <typename ValueType>
+inline void Object::Set(napi_value key, const ValueType& value) {
+ napi_status status =
+ napi_set_property(_env, _value, key, Value::From(_env, value));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+template <typename ValueType>
+inline void Object::Set(Value key, const ValueType& value) {
+ napi_status status =
+ napi_set_property(_env, _value, key, Value::From(_env, value));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+template <typename ValueType>
+inline void Object::Set(const char* utf8name, const ValueType& value) {
+ napi_status status =
+ napi_set_named_property(_env, _value, utf8name, Value::From(_env, value));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+template <typename ValueType>
+inline void Object::Set(const std::string& utf8name, const ValueType& value) {
+ Set(utf8name.c_str(), value);
+}
+
+inline bool Object::Delete(napi_value key) {
+ bool result;
+ napi_status status = napi_delete_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::Delete(Value key) {
+ bool result;
+ napi_status status = napi_delete_property(_env, _value, key, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline bool Object::Delete(const char* utf8name) {
+ return Delete(String::New(_env, utf8name));
+}
+
+inline bool Object::Delete(const std::string& utf8name) {
+ return Delete(String::New(_env, utf8name));
+}
+
+inline bool Object::Has(uint32_t index) const {
+ bool result;
+ napi_status status = napi_has_element(_env, _value, index, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline Value Object::Get(uint32_t index) const {
+ napi_value value;
+ napi_status status = napi_get_element(_env, _value, index, &value);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, value);
+}
+
+template <typename ValueType>
+inline void Object::Set(uint32_t index, const ValueType& value) {
+ napi_status status =
+ napi_set_element(_env, _value, index, Value::From(_env, value));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline bool Object::Delete(uint32_t index) {
+ bool result;
+ napi_status status = napi_delete_element(_env, _value, index, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+inline Array Object::GetPropertyNames() const {
+ napi_value result;
+ napi_status status = napi_get_property_names(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Array());
+ return Array(_env, result);
+}
+
+inline void Object::DefineProperty(const PropertyDescriptor& property) {
+ napi_status status = napi_define_properties(_env, _value, 1,
+ reinterpret_cast<const napi_property_descriptor*>(&property));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline void Object::DefineProperties(const std::initializer_list<PropertyDescriptor>& properties) {
+ napi_status status = napi_define_properties(_env, _value, properties.size(),
+ reinterpret_cast<const napi_property_descriptor*>(properties.begin()));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline void Object::DefineProperties(const std::vector<PropertyDescriptor>& properties) {
+ napi_status status = napi_define_properties(_env, _value, properties.size(),
+ reinterpret_cast<const napi_property_descriptor*>(properties.data()));
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline bool Object::InstanceOf(const Function& constructor) const {
+ bool result;
+ napi_status status = napi_instanceof(_env, _value, constructor, &result);
+ NAPI_THROW_IF_FAILED(_env, status, false);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// External class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline External<T> External<T>::New(napi_env env, T* data) {
+ napi_value value;
+ napi_status status = napi_create_external(env, data, nullptr, nullptr, &value);
+ NAPI_THROW_IF_FAILED(env, status, External());
+ return External(env, value);
+}
+
+template <typename T>
+template <typename Finalizer>
+inline External<T> External<T>::New(napi_env env,
+ T* data,
+ Finalizer finalizeCallback) {
+ napi_value value;
+ details::FinalizeData<T, Finalizer>* finalizeData =
+ new details::FinalizeData<T, Finalizer>({ finalizeCallback, nullptr });
+ napi_status status = napi_create_external(
+ env,
+ data,
+ details::FinalizeData<T, Finalizer>::Wrapper,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, External());
+ }
+ return External(env, value);
+}
+
+template <typename T>
+template <typename Finalizer, typename Hint>
+inline External<T> External<T>::New(napi_env env,
+ T* data,
+ Finalizer finalizeCallback,
+ Hint* finalizeHint) {
+ napi_value value;
+ details::FinalizeData<T, Finalizer, Hint>* finalizeData =
+ new details::FinalizeData<T, Finalizer, Hint>({ finalizeCallback, finalizeHint });
+ napi_status status = napi_create_external(
+ env,
+ data,
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, External());
+ }
+ return External(env, value);
+}
+
+template <typename T>
+inline External<T>::External() : Value() {
+}
+
+template <typename T>
+inline External<T>::External(napi_env env, napi_value value) : Value(env, value) {
+}
+
+template <typename T>
+inline T* External<T>::Data() const {
+ void* data;
+ napi_status status = napi_get_value_external(_env, _value, &data);
+ NAPI_THROW_IF_FAILED(_env, status, nullptr);
+ return reinterpret_cast<T*>(data);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Array class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Array Array::New(napi_env env) {
+ napi_value value;
+ napi_status status = napi_create_array(env, &value);
+ NAPI_THROW_IF_FAILED(env, status, Array());
+ return Array(env, value);
+}
+
+inline Array Array::New(napi_env env, size_t length) {
+ napi_value value;
+ napi_status status = napi_create_array_with_length(env, length, &value);
+ NAPI_THROW_IF_FAILED(env, status, Array());
+ return Array(env, value);
+}
+
+inline Array::Array() : Object() {
+}
+
+inline Array::Array(napi_env env, napi_value value) : Object(env, value) {
+}
+
+inline uint32_t Array::Length() const {
+ uint32_t result;
+ napi_status status = napi_get_array_length(_env, _value, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ArrayBuffer class
+////////////////////////////////////////////////////////////////////////////////
+
+inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) {
+ napi_value value;
+ void* data;
+ napi_status status = napi_create_arraybuffer(env, byteLength, &data, &value);
+ NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
+
+ return ArrayBuffer(env, value, data, byteLength);
+}
+
+inline ArrayBuffer ArrayBuffer::New(napi_env env,
+ void* externalData,
+ size_t byteLength) {
+ napi_value value;
+ napi_status status = napi_create_external_arraybuffer(
+ env, externalData, byteLength, nullptr, nullptr, &value);
+ NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
+
+ return ArrayBuffer(env, value, externalData, byteLength);
+}
+
+template <typename Finalizer>
+inline ArrayBuffer ArrayBuffer::New(napi_env env,
+ void* externalData,
+ size_t byteLength,
+ Finalizer finalizeCallback) {
+ napi_value value;
+ details::FinalizeData<void, Finalizer>* finalizeData =
+ new details::FinalizeData<void, Finalizer>({ finalizeCallback, nullptr });
+ napi_status status = napi_create_external_arraybuffer(
+ env,
+ externalData,
+ byteLength,
+ details::FinalizeData<void, Finalizer>::Wrapper,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
+ }
+
+ return ArrayBuffer(env, value, externalData, byteLength);
+}
+
+template <typename Finalizer, typename Hint>
+inline ArrayBuffer ArrayBuffer::New(napi_env env,
+ void* externalData,
+ size_t byteLength,
+ Finalizer finalizeCallback,
+ Hint* finalizeHint) {
+ napi_value value;
+ details::FinalizeData<void, Finalizer, Hint>* finalizeData =
+ new details::FinalizeData<void, Finalizer, Hint>({ finalizeCallback, finalizeHint });
+ napi_status status = napi_create_external_arraybuffer(
+ env,
+ externalData,
+ byteLength,
+ details::FinalizeData<void, Finalizer, Hint>::WrapperWithHint,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, ArrayBuffer());
+ }
+
+ return ArrayBuffer(env, value, externalData, byteLength);
+}
+
+inline ArrayBuffer::ArrayBuffer() : Object(), _data(nullptr), _length(0) {
+}
+
+inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value)
+ : Object(env, value), _data(nullptr), _length(0) {
+}
+
+inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value, void* data, size_t length)
+ : Object(env, value), _data(data), _length(length) {
+}
+
+inline void* ArrayBuffer::Data() {
+ EnsureInfo();
+ return _data;
+}
+
+inline size_t ArrayBuffer::ByteLength() {
+ EnsureInfo();
+ return _length;
+}
+
+inline void ArrayBuffer::EnsureInfo() const {
+ // The ArrayBuffer instance may have been constructed from a napi_value whose
+ // length/data are not yet known. Fetch and cache these values just once,
+ // since they can never change during the lifetime of the ArrayBuffer.
+ if (_data == nullptr) {
+ napi_status status = napi_get_arraybuffer_info(_env, _value, &_data, &_length);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DataView class
+////////////////////////////////////////////////////////////////////////////////
+inline DataView DataView::New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer) {
+ return New(env, arrayBuffer, 0, arrayBuffer.ByteLength());
+}
+
+inline DataView DataView::New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer,
+ size_t byteOffset) {
+ if (byteOffset > arrayBuffer.ByteLength()) {
+ NAPI_THROW(RangeError::New(env,
+ "Start offset is outside the bounds of the buffer"),
+ DataView());
+ }
+ return New(env, arrayBuffer, byteOffset,
+ arrayBuffer.ByteLength() - byteOffset);
+}
+
+inline DataView DataView::New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer,
+ size_t byteOffset,
+ size_t byteLength) {
+ if (byteOffset + byteLength > arrayBuffer.ByteLength()) {
+ NAPI_THROW(RangeError::New(env, "Invalid DataView length"),
+ DataView());
+ }
+ napi_value value;
+ napi_status status = napi_create_dataview(
+ env, byteLength, arrayBuffer, byteOffset, &value);
+ NAPI_THROW_IF_FAILED(env, status, DataView());
+ return DataView(env, value);
+}
+
+inline DataView::DataView() : Object() {
+}
+
+inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
+ napi_status status = napi_get_dataview_info(
+ _env,
+ _value /* dataView */,
+ &_length /* byteLength */,
+ &_data /* data */,
+ nullptr /* arrayBuffer */,
+ nullptr /* byteOffset */);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline Napi::ArrayBuffer DataView::ArrayBuffer() const {
+ napi_value arrayBuffer;
+ napi_status status = napi_get_dataview_info(
+ _env,
+ _value /* dataView */,
+ nullptr /* byteLength */,
+ nullptr /* data */,
+ &arrayBuffer /* arrayBuffer */,
+ nullptr /* byteOffset */);
+ NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer());
+ return Napi::ArrayBuffer(_env, arrayBuffer);
+}
+
+inline size_t DataView::ByteOffset() const {
+ size_t byteOffset;
+ napi_status status = napi_get_dataview_info(
+ _env,
+ _value /* dataView */,
+ nullptr /* byteLength */,
+ nullptr /* data */,
+ nullptr /* arrayBuffer */,
+ &byteOffset /* byteOffset */);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return byteOffset;
+}
+
+inline size_t DataView::ByteLength() const {
+ return _length;
+}
+
+inline void* DataView::Data() const {
+ return _data;
+}
+
+inline float DataView::GetFloat32(size_t byteOffset) const {
+ return ReadData<float>(byteOffset);
+}
+
+inline double DataView::GetFloat64(size_t byteOffset) const {
+ return ReadData<double>(byteOffset);
+}
+
+inline int8_t DataView::GetInt8(size_t byteOffset) const {
+ return ReadData<int8_t>(byteOffset);
+}
+
+inline int16_t DataView::GetInt16(size_t byteOffset) const {
+ return ReadData<int16_t>(byteOffset);
+}
+
+inline int32_t DataView::GetInt32(size_t byteOffset) const {
+ return ReadData<int32_t>(byteOffset);
+}
+
+inline uint8_t DataView::GetUint8(size_t byteOffset) const {
+ return ReadData<uint8_t>(byteOffset);
+}
+
+inline uint16_t DataView::GetUint16(size_t byteOffset) const {
+ return ReadData<uint16_t>(byteOffset);
+}
+
+inline uint32_t DataView::GetUint32(size_t byteOffset) const {
+ return ReadData<uint32_t>(byteOffset);
+}
+
+inline void DataView::SetFloat32(size_t byteOffset, float value) const {
+ WriteData<float>(byteOffset, value);
+}
+
+inline void DataView::SetFloat64(size_t byteOffset, double value) const {
+ WriteData<double>(byteOffset, value);
+}
+
+inline void DataView::SetInt8(size_t byteOffset, int8_t value) const {
+ WriteData<int8_t>(byteOffset, value);
+}
+
+inline void DataView::SetInt16(size_t byteOffset, int16_t value) const {
+ WriteData<int16_t>(byteOffset, value);
+}
+
+inline void DataView::SetInt32(size_t byteOffset, int32_t value) const {
+ WriteData<int32_t>(byteOffset, value);
+}
+
+inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const {
+ WriteData<uint8_t>(byteOffset, value);
+}
+
+inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const {
+ WriteData<uint16_t>(byteOffset, value);
+}
+
+inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const {
+ WriteData<uint32_t>(byteOffset, value);
+}
+
+template <typename T>
+inline T DataView::ReadData(size_t byteOffset) const {
+ if (byteOffset + sizeof(T) > _length ||
+ byteOffset + sizeof(T) < byteOffset) { // overflow
+ NAPI_THROW(RangeError::New(_env,
+ "Offset is outside the bounds of the DataView"), 0);
+ }
+
+ return *reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset);
+}
+
+template <typename T>
+inline void DataView::WriteData(size_t byteOffset, T value) const {
+ if (byteOffset + sizeof(T) > _length ||
+ byteOffset + sizeof(T) < byteOffset) { // overflow
+ NAPI_THROW_VOID(RangeError::New(_env,
+ "Offset is outside the bounds of the DataView"));
+ }
+
+ *reinterpret_cast<T*>(static_cast<uint8_t*>(_data) + byteOffset) = value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TypedArray class
+////////////////////////////////////////////////////////////////////////////////
+
+inline TypedArray::TypedArray()
+ : Object(), _type(TypedArray::unknown_array_type), _length(0) {
+}
+
+inline TypedArray::TypedArray(napi_env env, napi_value value)
+ : Object(env, value), _type(TypedArray::unknown_array_type), _length(0) {
+}
+
+inline TypedArray::TypedArray(napi_env env,
+ napi_value value,
+ napi_typedarray_type type,
+ size_t length)
+ : Object(env, value), _type(type), _length(length) {
+}
+
+inline napi_typedarray_type TypedArray::TypedArrayType() const {
+ if (_type == TypedArray::unknown_array_type) {
+ napi_status status = napi_get_typedarray_info(_env, _value,
+ &const_cast<TypedArray*>(this)->_type, &const_cast<TypedArray*>(this)->_length,
+ nullptr, nullptr, nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, napi_int8_array);
+ }
+
+ return _type;
+}
+
+inline uint8_t TypedArray::ElementSize() const {
+ switch (TypedArrayType()) {
+ case napi_int8_array:
+ case napi_uint8_array:
+ case napi_uint8_clamped_array:
+ return 1;
+ case napi_int16_array:
+ case napi_uint16_array:
+ return 2;
+ case napi_int32_array:
+ case napi_uint32_array:
+ case napi_float32_array:
+ return 4;
+ case napi_float64_array:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+inline size_t TypedArray::ElementLength() const {
+ if (_type == TypedArray::unknown_array_type) {
+ napi_status status = napi_get_typedarray_info(_env, _value,
+ &const_cast<TypedArray*>(this)->_type, &const_cast<TypedArray*>(this)->_length,
+ nullptr, nullptr, nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ }
+
+ return _length;
+}
+
+inline size_t TypedArray::ByteOffset() const {
+ size_t byteOffset;
+ napi_status status = napi_get_typedarray_info(
+ _env, _value, nullptr, nullptr, nullptr, nullptr, &byteOffset);
+ NAPI_THROW_IF_FAILED(_env, status, 0);
+ return byteOffset;
+}
+
+inline size_t TypedArray::ByteLength() const {
+ return ElementSize() * ElementLength();
+}
+
+inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const {
+ napi_value arrayBuffer;
+ napi_status status = napi_get_typedarray_info(
+ _env, _value, nullptr, nullptr, nullptr, &arrayBuffer, nullptr);
+ NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer());
+ return Napi::ArrayBuffer(_env, arrayBuffer);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TypedArrayOf<T> class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
+ size_t elementLength,
+ napi_typedarray_type type) {
+ Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(env, elementLength * sizeof (T));
+ return New(env, elementLength, arrayBuffer, 0, type);
+}
+
+template <typename T>
+inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
+ size_t elementLength,
+ Napi::ArrayBuffer arrayBuffer,
+ size_t bufferOffset,
+ napi_typedarray_type type) {
+ napi_value value;
+ napi_status status = napi_create_typedarray(
+ env, type, elementLength, arrayBuffer, bufferOffset, &value);
+ NAPI_THROW_IF_FAILED(env, status, TypedArrayOf<T>());
+
+ return TypedArrayOf<T>(
+ env, value, type, elementLength,
+ reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(arrayBuffer.Data()) + bufferOffset));
+}
+
+template <typename T>
+inline TypedArrayOf<T>::TypedArrayOf() : TypedArray(), _data(nullptr) {
+}
+
+template <typename T>
+inline TypedArrayOf<T>::TypedArrayOf(napi_env env, napi_value value)
+ : TypedArray(env, value), _data(nullptr) {
+ napi_status status = napi_get_typedarray_info(
+ _env, _value, &_type, &_length, reinterpret_cast<void**>(&_data), nullptr, nullptr);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+template <typename T>
+inline TypedArrayOf<T>::TypedArrayOf(napi_env env,
+ napi_value value,
+ napi_typedarray_type type,
+ size_t length,
+ T* data)
+ : TypedArray(env, value, type, length), _data(data) {
+ if (!(type == TypedArrayTypeForPrimitiveType<T>() ||
+ (type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value))) {
+ NAPI_THROW_VOID(TypeError::New(env, "Array type must match the template parameter. "
+ "(Uint8 arrays may optionally have the \"clamped\" array type.)"));
+ }
+}
+
+template <typename T>
+inline T& TypedArrayOf<T>::operator [](size_t index) {
+ return _data[index];
+}
+
+template <typename T>
+inline const T& TypedArrayOf<T>::operator [](size_t index) const {
+ return _data[index];
+}
+
+template <typename T>
+inline T* TypedArrayOf<T>::Data() {
+ return _data;
+}
+
+template <typename T>
+inline const T* TypedArrayOf<T>::Data() const {
+ return _data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Function class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename CbData>
+static inline napi_status
+CreateFunction(napi_env env,
+ const char* utf8name,
+ napi_callback cb,
+ CbData* data,
+ napi_value* result) {
+ napi_status status =
+ napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, cb, data, result);
+ if (status == napi_ok) {
+ status = Napi::details::AttachData(env, *result, data);
+ }
+
+ return status;
+}
+
+template <typename Callable>
+inline Function Function::New(napi_env env,
+ Callable cb,
+ const char* utf8name,
+ void* data) {
+ typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType;
+ typedef details::CallbackData<Callable, ReturnType> CbData;
+ auto callbackData = new CbData({ cb, data });
+
+ napi_value value;
+ napi_status status = CreateFunction(env,
+ utf8name,
+ CbData::Wrapper,
+ callbackData,
+ &value);
+ if (status != napi_ok) {
+ delete callbackData;
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ }
+
+ return Function(env, value);
+}
+
+template <typename Callable>
+inline Function Function::New(napi_env env,
+ Callable cb,
+ const std::string& utf8name,
+ void* data) {
+ return New(env, cb, utf8name.c_str(), data);
+}
+
+inline Function::Function() : Object() {
+}
+
+inline Function::Function(napi_env env, napi_value value) : Object(env, value) {
+}
+
+inline Value Function::operator ()(const std::initializer_list<napi_value>& args) const {
+ return Call(Env().Undefined(), args);
+}
+
+inline Value Function::Call(const std::initializer_list<napi_value>& args) const {
+ return Call(Env().Undefined(), args);
+}
+
+inline Value Function::Call(const std::vector<napi_value>& args) const {
+ return Call(Env().Undefined(), args);
+}
+
+inline Value Function::Call(size_t argc, const napi_value* args) const {
+ return Call(Env().Undefined(), argc, args);
+}
+
+inline Value Function::Call(napi_value recv, const std::initializer_list<napi_value>& args) const {
+ return Call(recv, args.size(), args.begin());
+}
+
+inline Value Function::Call(napi_value recv, const std::vector<napi_value>& args) const {
+ return Call(recv, args.size(), args.data());
+}
+
+inline Value Function::Call(napi_value recv, size_t argc, const napi_value* args) const {
+ napi_value result;
+ napi_status status = napi_call_function(
+ _env, recv, _value, argc, args, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+inline Value Function::MakeCallback(
+ napi_value recv,
+ const std::initializer_list<napi_value>& args,
+ napi_async_context context) const {
+ return MakeCallback(recv, args.size(), args.begin(), context);
+}
+
+inline Value Function::MakeCallback(
+ napi_value recv,
+ const std::vector<napi_value>& args,
+ napi_async_context context) const {
+ return MakeCallback(recv, args.size(), args.data(), context);
+}
+
+inline Value Function::MakeCallback(
+ napi_value recv,
+ size_t argc,
+ const napi_value* args,
+ napi_async_context context) const {
+ napi_value result;
+ napi_status status = napi_make_callback(
+ _env, context, recv, _value, argc, args, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+inline Object Function::New(const std::initializer_list<napi_value>& args) const {
+ return New(args.size(), args.begin());
+}
+
+inline Object Function::New(const std::vector<napi_value>& args) const {
+ return New(args.size(), args.data());
+}
+
+inline Object Function::New(size_t argc, const napi_value* args) const {
+ napi_value result;
+ napi_status status = napi_new_instance(
+ _env, _value, argc, args, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Object());
+ return Object(_env, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Promise class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Promise::Deferred Promise::Deferred::New(napi_env env) {
+ return Promise::Deferred(env);
+}
+
+inline Promise::Deferred::Deferred(napi_env env) : _env(env) {
+ napi_status status = napi_create_promise(_env, &_deferred, &_promise);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline Promise Promise::Deferred::Promise() const {
+ return Napi::Promise(_env, _promise);
+}
+
+inline Napi::Env Promise::Deferred::Env() const {
+ return Napi::Env(_env);
+}
+
+inline void Promise::Deferred::Resolve(napi_value value) const {
+ napi_status status = napi_resolve_deferred(_env, _deferred, value);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline void Promise::Deferred::Reject(napi_value value) const {
+ napi_status status = napi_reject_deferred(_env, _deferred, value);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Buffer<T> class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline Buffer<T> Buffer<T>::New(napi_env env, size_t length) {
+ napi_value value;
+ void* data;
+ napi_status status = napi_create_buffer(env, length * sizeof (T), &data, &value);
+ NAPI_THROW_IF_FAILED(env, status, Buffer<T>());
+ return Buffer(env, value, length, static_cast<T*>(data));
+}
+
+template <typename T>
+inline Buffer<T> Buffer<T>::New(napi_env env, T* data, size_t length) {
+ napi_value value;
+ napi_status status = napi_create_external_buffer(
+ env, length * sizeof (T), data, nullptr, nullptr, &value);
+ NAPI_THROW_IF_FAILED(env, status, Buffer<T>());
+ return Buffer(env, value, length, data);
+}
+
+template <typename T>
+template <typename Finalizer>
+inline Buffer<T> Buffer<T>::New(napi_env env,
+ T* data,
+ size_t length,
+ Finalizer finalizeCallback) {
+ napi_value value;
+ details::FinalizeData<T, Finalizer>* finalizeData =
+ new details::FinalizeData<T, Finalizer>({ finalizeCallback, nullptr });
+ napi_status status = napi_create_external_buffer(
+ env,
+ length * sizeof (T),
+ data,
+ details::FinalizeData<T, Finalizer>::Wrapper,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, Buffer());
+ }
+ return Buffer(env, value, length, data);
+}
+
+template <typename T>
+template <typename Finalizer, typename Hint>
+inline Buffer<T> Buffer<T>::New(napi_env env,
+ T* data,
+ size_t length,
+ Finalizer finalizeCallback,
+ Hint* finalizeHint) {
+ napi_value value;
+ details::FinalizeData<T, Finalizer, Hint>* finalizeData =
+ new details::FinalizeData<T, Finalizer, Hint>({ finalizeCallback, finalizeHint });
+ napi_status status = napi_create_external_buffer(
+ env,
+ length * sizeof (T),
+ data,
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
+ finalizeData,
+ &value);
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, Buffer());
+ }
+ return Buffer(env, value, length, data);
+}
+
+template <typename T>
+inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) {
+ napi_value value;
+ napi_status status = napi_create_buffer_copy(
+ env, length * sizeof (T), data, nullptr, &value);
+ NAPI_THROW_IF_FAILED(env, status, Buffer<T>());
+ return Buffer<T>(env, value);
+}
+
+template <typename T>
+inline Buffer<T>::Buffer() : Uint8Array(), _length(0), _data(nullptr) {
+}
+
+template <typename T>
+inline Buffer<T>::Buffer(napi_env env, napi_value value)
+ : Uint8Array(env, value), _length(0), _data(nullptr) {
+}
+
+template <typename T>
+inline Buffer<T>::Buffer(napi_env env, napi_value value, size_t length, T* data)
+ : Uint8Array(env, value), _length(length), _data(data) {
+}
+
+template <typename T>
+inline size_t Buffer<T>::Length() const {
+ EnsureInfo();
+ return _length;
+}
+
+template <typename T>
+inline T* Buffer<T>::Data() const {
+ EnsureInfo();
+ return _data;
+}
+
+template <typename T>
+inline void Buffer<T>::EnsureInfo() const {
+ // The Buffer instance may have been constructed from a napi_value whose
+ // length/data are not yet known. Fetch and cache these values just once,
+ // since they can never change during the lifetime of the Buffer.
+ if (_data == nullptr) {
+ size_t byteLength;
+ void* voidData;
+ napi_status status = napi_get_buffer_info(_env, _value, &voidData, &byteLength);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+ _length = byteLength / sizeof (T);
+ _data = static_cast<T*>(voidData);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Error class
+////////////////////////////////////////////////////////////////////////////////
+
+inline Error Error::New(napi_env env) {
+ napi_status status;
+ napi_value error = nullptr;
+
+ const napi_extended_error_info* info;
+ status = napi_get_last_error_info(env, &info);
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_last_error_info");
+
+ if (status == napi_ok) {
+ if (info->error_code == napi_pending_exception) {
+ status = napi_get_and_clear_last_exception(env, &error);
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_and_clear_last_exception");
+ }
+ else {
+ const char* error_message = info->error_message != nullptr ?
+ info->error_message : "Error in native callback";
+
+ bool isExceptionPending;
+ status = napi_is_exception_pending(env, &isExceptionPending);
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_is_exception_pending");
+
+ if (isExceptionPending) {
+ status = napi_get_and_clear_last_exception(env, &error);
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_and_clear_last_exception");
+ }
+
+ napi_value message;
+ status = napi_create_string_utf8(
+ env,
+ error_message,
+ std::strlen(error_message),
+ &message);
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_string_utf8");
+
+ if (status == napi_ok) {
+ switch (info->error_code) {
+ case napi_object_expected:
+ case napi_string_expected:
+ case napi_boolean_expected:
+ case napi_number_expected:
+ status = napi_create_type_error(env, nullptr, message, &error);
+ break;
+ default:
+ status = napi_create_error(env, nullptr, message, &error);
+ break;
+ }
+ NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_error");
+ }
+ }
+ }
+
+ return Error(env, error);
+}
+
+inline Error Error::New(napi_env env, const char* message) {
+ return Error::New<Error>(env, message, std::strlen(message), napi_create_error);
+}
+
+inline Error Error::New(napi_env env, const std::string& message) {
+ return Error::New<Error>(env, message.c_str(), message.size(), napi_create_error);
+}
+
+inline NAPI_NO_RETURN void Error::Fatal(const char* location, const char* message) {
+ napi_fatal_error(location, NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH);
+}
+
+inline Error::Error() : ObjectReference() {
+}
+
+inline Error::Error(napi_env env, napi_value value) : ObjectReference(env, nullptr) {
+ if (value != nullptr) {
+ napi_status status = napi_create_reference(env, value, 1, &_ref);
+
+ // Avoid infinite recursion in the failure case.
+ // Don't try to construct & throw another Error instance.
+ NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_reference");
+ }
+}
+
+inline Error::Error(Error&& other) : ObjectReference(std::move(other)) {
+}
+
+inline Error& Error::operator =(Error&& other) {
+ static_cast<Reference<Object>*>(this)->operator=(std::move(other));
+ return *this;
+}
+
+inline Error::Error(const Error& other) : ObjectReference(other) {
+}
+
+inline Error& Error::operator =(Error& other) {
+ Reset();
+
+ _env = other.Env();
+ HandleScope scope(_env);
+
+ napi_value value = other.Value();
+ if (value != nullptr) {
+ napi_status status = napi_create_reference(_env, value, 1, &_ref);
+ NAPI_THROW_IF_FAILED(_env, status, *this);
+ }
+
+ return *this;
+}
+
+inline const std::string& Error::Message() const NAPI_NOEXCEPT {
+ if (_message.size() == 0 && _env != nullptr) {
+#ifdef NAPI_CPP_EXCEPTIONS
+ try {
+ _message = Get("message").As<String>();
+ }
+ catch (...) {
+ // Catch all errors here, to include e.g. a std::bad_alloc from
+ // the std::string::operator=, because this method may not throw.
+ }
+#else // NAPI_CPP_EXCEPTIONS
+ _message = Get("message").As<String>();
+#endif // NAPI_CPP_EXCEPTIONS
+ }
+ return _message;
+}
+
+inline void Error::ThrowAsJavaScriptException() const {
+ HandleScope scope(_env);
+ if (!IsEmpty()) {
+
+ // We intentionally don't use `NAPI_THROW_*` macros here to ensure
+ // that there is no possible recursion as `ThrowAsJavaScriptException`
+ // is part of `NAPI_THROW_*` macro definition for noexcept.
+
+ napi_status status = napi_throw(_env, Value());
+
+#ifdef NAPI_CPP_EXCEPTIONS
+ if (status != napi_ok) {
+ throw Error::New(_env);
+ }
+#else // NAPI_CPP_EXCEPTIONS
+ NAPI_FATAL_IF_FAILED(status, "Error::ThrowAsJavaScriptException", "napi_throw");
+#endif // NAPI_CPP_EXCEPTIONS
+ }
+}
+
+#ifdef NAPI_CPP_EXCEPTIONS
+
+inline const char* Error::what() const NAPI_NOEXCEPT {
+ return Message().c_str();
+}
+
+#endif // NAPI_CPP_EXCEPTIONS
+
+template <typename TError>
+inline TError Error::New(napi_env env,
+ const char* message,
+ size_t length,
+ create_error_fn create_error) {
+ napi_value str;
+ napi_status status = napi_create_string_utf8(env, message, length, &str);
+ NAPI_THROW_IF_FAILED(env, status, TError());
+
+ napi_value error;
+ status = create_error(env, nullptr, str, &error);
+ NAPI_THROW_IF_FAILED(env, status, TError());
+
+ return TError(env, error);
+}
+
+inline TypeError TypeError::New(napi_env env, const char* message) {
+ return Error::New<TypeError>(env, message, std::strlen(message), napi_create_type_error);
+}
+
+inline TypeError TypeError::New(napi_env env, const std::string& message) {
+ return Error::New<TypeError>(env, message.c_str(), message.size(), napi_create_type_error);
+}
+
+inline TypeError::TypeError() : Error() {
+}
+
+inline TypeError::TypeError(napi_env env, napi_value value) : Error(env, value) {
+}
+
+inline RangeError RangeError::New(napi_env env, const char* message) {
+ return Error::New<RangeError>(env, message, std::strlen(message), napi_create_range_error);
+}
+
+inline RangeError RangeError::New(napi_env env, const std::string& message) {
+ return Error::New<RangeError>(env, message.c_str(), message.size(), napi_create_range_error);
+}
+
+inline RangeError::RangeError() : Error() {
+}
+
+inline RangeError::RangeError(napi_env env, napi_value value) : Error(env, value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Reference<T> class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline Reference<T> Reference<T>::New(const T& value, uint32_t initialRefcount) {
+ napi_env env = value.Env();
+ napi_value val = value;
+
+ if (val == nullptr) {
+ return Reference<T>(env, nullptr);
+ }
+
+ napi_ref ref;
+ napi_status status = napi_create_reference(env, value, initialRefcount, &ref);
+ NAPI_THROW_IF_FAILED(env, status, Reference<T>());
+
+ return Reference<T>(env, ref);
+}
+
+
+template <typename T>
+inline Reference<T>::Reference() : _env(nullptr), _ref(nullptr), _suppressDestruct(false) {
+}
+
+template <typename T>
+inline Reference<T>::Reference(napi_env env, napi_ref ref)
+ : _env(env), _ref(ref), _suppressDestruct(false) {
+}
+
+template <typename T>
+inline Reference<T>::~Reference() {
+ if (_ref != nullptr) {
+ if (!_suppressDestruct) {
+ napi_delete_reference(_env, _ref);
+ }
+
+ _ref = nullptr;
+ }
+}
+
+template <typename T>
+inline Reference<T>::Reference(Reference<T>&& other)
+ : _env(other._env), _ref(other._ref), _suppressDestruct(other._suppressDestruct) {
+ other._env = nullptr;
+ other._ref = nullptr;
+ other._suppressDestruct = false;
+}
+
+template <typename T>
+inline Reference<T>& Reference<T>::operator =(Reference<T>&& other) {
+ Reset();
+ _env = other._env;
+ _ref = other._ref;
+ _suppressDestruct = other._suppressDestruct;
+ other._env = nullptr;
+ other._ref = nullptr;
+ other._suppressDestruct = false;
+ return *this;
+}
+
+template <typename T>
+inline Reference<T>::Reference(const Reference<T>& other)
+ : _env(other._env), _ref(nullptr), _suppressDestruct(false) {
+ HandleScope scope(_env);
+
+ napi_value value = other.Value();
+ if (value != nullptr) {
+ // Copying is a limited scenario (currently only used for Error object) and always creates a
+ // strong reference to the given value even if the incoming reference is weak.
+ napi_status status = napi_create_reference(_env, value, 1, &_ref);
+ NAPI_FATAL_IF_FAILED(status, "Reference<T>::Reference", "napi_create_reference");
+ }
+}
+
+template <typename T>
+inline Reference<T>::operator napi_ref() const {
+ return _ref;
+}
+
+template <typename T>
+inline bool Reference<T>::operator ==(const Reference<T> &other) const {
+ HandleScope scope(_env);
+ return this->Value().StrictEquals(other.Value());
+}
+
+template <typename T>
+inline bool Reference<T>::operator !=(const Reference<T> &other) const {
+ return !this->operator ==(other);
+}
+
+template <typename T>
+inline Napi::Env Reference<T>::Env() const {
+ return Napi::Env(_env);
+}
+
+template <typename T>
+inline bool Reference<T>::IsEmpty() const {
+ return _ref == nullptr;
+}
+
+template <typename T>
+inline T Reference<T>::Value() const {
+ if (_ref == nullptr) {
+ return T(_env, nullptr);
+ }
+
+ napi_value value;
+ napi_status status = napi_get_reference_value(_env, _ref, &value);
+ NAPI_THROW_IF_FAILED(_env, status, T());
+ return T(_env, value);
+}
+
+template <typename T>
+inline uint32_t Reference<T>::Ref() {
+ uint32_t result;
+ napi_status status = napi_reference_ref(_env, _ref, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 1);
+ return result;
+}
+
+template <typename T>
+inline uint32_t Reference<T>::Unref() {
+ uint32_t result;
+ napi_status status = napi_reference_unref(_env, _ref, &result);
+ NAPI_THROW_IF_FAILED(_env, status, 1);
+ return result;
+}
+
+template <typename T>
+inline void Reference<T>::Reset() {
+ if (_ref != nullptr) {
+ napi_status status = napi_delete_reference(_env, _ref);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+ _ref = nullptr;
+ }
+}
+
+template <typename T>
+inline void Reference<T>::Reset(const T& value, uint32_t refcount) {
+ Reset();
+ _env = value.Env();
+
+ napi_value val = value;
+ if (val != nullptr) {
+ napi_status status = napi_create_reference(_env, value, refcount, &_ref);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+ }
+}
+
+template <typename T>
+inline void Reference<T>::SuppressDestruct() {
+ _suppressDestruct = true;
+}
+
+template <typename T>
+inline Reference<T> Weak(T value) {
+ return Reference<T>::New(value, 0);
+}
+
+inline ObjectReference Weak(Object value) {
+ return Reference<Object>::New(value, 0);
+}
+
+inline FunctionReference Weak(Function value) {
+ return Reference<Function>::New(value, 0);
+}
+
+template <typename T>
+inline Reference<T> Persistent(T value) {
+ return Reference<T>::New(value, 1);
+}
+
+inline ObjectReference Persistent(Object value) {
+ return Reference<Object>::New(value, 1);
+}
+
+inline FunctionReference Persistent(Function value) {
+ return Reference<Function>::New(value, 1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ObjectReference class
+////////////////////////////////////////////////////////////////////////////////
+
+inline ObjectReference::ObjectReference(): Reference<Object>() {
+}
+
+inline ObjectReference::ObjectReference(napi_env env, napi_ref ref): Reference<Object>(env, ref) {
+}
+
+inline ObjectReference::ObjectReference(Reference<Object>&& other)
+ : Reference<Object>(std::move(other)) {
+}
+
+inline ObjectReference& ObjectReference::operator =(Reference<Object>&& other) {
+ static_cast<Reference<Object>*>(this)->operator=(std::move(other));
+ return *this;
+}
+
+inline ObjectReference::ObjectReference(ObjectReference&& other)
+ : Reference<Object>(std::move(other)) {
+}
+
+inline ObjectReference& ObjectReference::operator =(ObjectReference&& other) {
+ static_cast<Reference<Object>*>(this)->operator=(std::move(other));
+ return *this;
+}
+
+inline ObjectReference::ObjectReference(const ObjectReference& other)
+ : Reference<Object>(other) {
+}
+
+inline Napi::Value ObjectReference::Get(const char* utf8name) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value().Get(utf8name));
+}
+
+inline Napi::Value ObjectReference::Get(const std::string& utf8name) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value().Get(utf8name));
+}
+
+inline void ObjectReference::Set(const char* utf8name, napi_value value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, value);
+}
+
+inline void ObjectReference::Set(const char* utf8name, Napi::Value value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, value);
+}
+
+inline void ObjectReference::Set(const char* utf8name, const char* utf8value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, utf8value);
+}
+
+inline void ObjectReference::Set(const char* utf8name, bool boolValue) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, boolValue);
+}
+
+inline void ObjectReference::Set(const char* utf8name, double numberValue) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, numberValue);
+}
+
+inline void ObjectReference::Set(const std::string& utf8name, napi_value value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, value);
+}
+
+inline void ObjectReference::Set(const std::string& utf8name, Napi::Value value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, value);
+}
+
+inline void ObjectReference::Set(const std::string& utf8name, std::string& utf8value) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, utf8value);
+}
+
+inline void ObjectReference::Set(const std::string& utf8name, bool boolValue) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, boolValue);
+}
+
+inline void ObjectReference::Set(const std::string& utf8name, double numberValue) {
+ HandleScope scope(_env);
+ Value().Set(utf8name, numberValue);
+}
+
+inline Napi::Value ObjectReference::Get(uint32_t index) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value().Get(index));
+}
+
+inline void ObjectReference::Set(uint32_t index, napi_value value) {
+ HandleScope scope(_env);
+ Value().Set(index, value);
+}
+
+inline void ObjectReference::Set(uint32_t index, Napi::Value value) {
+ HandleScope scope(_env);
+ Value().Set(index, value);
+}
+
+inline void ObjectReference::Set(uint32_t index, const char* utf8value) {
+ HandleScope scope(_env);
+ Value().Set(index, utf8value);
+}
+
+inline void ObjectReference::Set(uint32_t index, const std::string& utf8value) {
+ HandleScope scope(_env);
+ Value().Set(index, utf8value);
+}
+
+inline void ObjectReference::Set(uint32_t index, bool boolValue) {
+ HandleScope scope(_env);
+ Value().Set(index, boolValue);
+}
+
+inline void ObjectReference::Set(uint32_t index, double numberValue) {
+ HandleScope scope(_env);
+ Value().Set(index, numberValue);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FunctionReference class
+////////////////////////////////////////////////////////////////////////////////
+
+inline FunctionReference::FunctionReference(): Reference<Function>() {
+}
+
+inline FunctionReference::FunctionReference(napi_env env, napi_ref ref)
+ : Reference<Function>(env, ref) {
+}
+
+inline FunctionReference::FunctionReference(Reference<Function>&& other)
+ : Reference<Function>(std::move(other)) {
+}
+
+inline FunctionReference& FunctionReference::operator =(Reference<Function>&& other) {
+ static_cast<Reference<Function>*>(this)->operator=(std::move(other));
+ return *this;
+}
+
+inline FunctionReference::FunctionReference(FunctionReference&& other)
+ : Reference<Function>(std::move(other)) {
+}
+
+inline FunctionReference& FunctionReference::operator =(FunctionReference&& other) {
+ static_cast<Reference<Function>*>(this)->operator=(std::move(other));
+ return *this;
+}
+
+inline Napi::Value FunctionReference::operator ()(
+ const std::initializer_list<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value()(args));
+}
+
+inline Napi::Value FunctionReference::Call(const std::initializer_list<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().Call(args);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::Call(const std::vector<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().Call(args);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::Call(
+ napi_value recv, const std::initializer_list<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().Call(recv, args);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::Call(
+ napi_value recv, const std::vector<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().Call(recv, args);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::Call(
+ napi_value recv, size_t argc, const napi_value* args) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().Call(recv, argc, args);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::MakeCallback(
+ napi_value recv,
+ const std::initializer_list<napi_value>& args,
+ napi_async_context context) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().MakeCallback(recv, args, context);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::MakeCallback(
+ napi_value recv,
+ const std::vector<napi_value>& args,
+ napi_async_context context) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().MakeCallback(recv, args, context);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Napi::Value FunctionReference::MakeCallback(
+ napi_value recv,
+ size_t argc,
+ const napi_value* args,
+ napi_async_context context) const {
+ EscapableHandleScope scope(_env);
+ Napi::Value result = Value().MakeCallback(recv, argc, args, context);
+ if (scope.Env().IsExceptionPending()) {
+ return Value();
+ }
+ return scope.Escape(result);
+}
+
+inline Object FunctionReference::New(const std::initializer_list<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value().New(args)).As<Object>();
+}
+
+inline Object FunctionReference::New(const std::vector<napi_value>& args) const {
+ EscapableHandleScope scope(_env);
+ return scope.Escape(Value().New(args)).As<Object>();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CallbackInfo class
+////////////////////////////////////////////////////////////////////////////////
+
+inline CallbackInfo::CallbackInfo(napi_env env, napi_callback_info info)
+ : _env(env), _info(info), _this(nullptr), _dynamicArgs(nullptr), _data(nullptr) {
+ _argc = _staticArgCount;
+ _argv = _staticArgs;
+ napi_status status = napi_get_cb_info(env, info, &_argc, _argv, &_this, &_data);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+
+ if (_argc > _staticArgCount) {
+ // Use either a fixed-size array (on the stack) or a dynamically-allocated
+ // array (on the heap) depending on the number of args.
+ _dynamicArgs = new napi_value[_argc];
+ _argv = _dynamicArgs;
+
+ status = napi_get_cb_info(env, info, &_argc, _argv, nullptr, nullptr);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+ }
+}
+
+inline CallbackInfo::~CallbackInfo() {
+ if (_dynamicArgs != nullptr) {
+ delete[] _dynamicArgs;
+ }
+}
+
+inline Value CallbackInfo::NewTarget() const {
+ napi_value newTarget;
+ napi_status status = napi_get_new_target(_env, _info, &newTarget);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, newTarget);
+}
+
+inline bool CallbackInfo::IsConstructCall() const {
+ return !NewTarget().IsEmpty();
+}
+
+inline Napi::Env CallbackInfo::Env() const {
+ return Napi::Env(_env);
+}
+
+inline size_t CallbackInfo::Length() const {
+ return _argc;
+}
+
+inline const Value CallbackInfo::operator [](size_t index) const {
+ return index < _argc ? Value(_env, _argv[index]) : Env().Undefined();
+}
+
+inline Value CallbackInfo::This() const {
+ if (_this == nullptr) {
+ return Env().Undefined();
+ }
+ return Object(_env, _this);
+}
+
+inline void* CallbackInfo::Data() const {
+ return _data;
+}
+
+inline void CallbackInfo::SetData(void* data) {
+ _data = data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PropertyDescriptor class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename Getter>
+inline PropertyDescriptor
+PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ const char* utf8name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* data) {
+ typedef details::CallbackData<Getter, Napi::Value> CbData;
+ auto callbackData = new CbData({ getter, data });
+
+ napi_status status = AttachData(env, object, callbackData);
+ if (status != napi_ok) {
+ delete callbackData;
+ NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor());
+ }
+
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ nullptr,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* data) {
+ return Accessor(env, object, utf8name.c_str(), getter, attributes, data);
+}
+
+template <typename Getter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ Name name,
+ Getter getter,
+ napi_property_attributes attributes,
+ void* data) {
+ typedef details::CallbackData<Getter, Napi::Value> CbData;
+ auto callbackData = new CbData({ getter, data });
+
+ napi_status status = AttachData(env, object, callbackData);
+ if (status != napi_ok) {
+ delete callbackData;
+ NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor());
+ }
+
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ nullptr,
+ CbData::Wrapper,
+ nullptr,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ const char* utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* data) {
+ typedef details::AccessorCallbackData<Getter, Setter> CbData;
+ auto callbackData = new CbData({ getter, setter, data });
+
+ napi_status status = AttachData(env, object, callbackData);
+ if (status != napi_ok) {
+ delete callbackData;
+ NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor());
+ }
+
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ nullptr,
+ CbData::GetterWrapper,
+ CbData::SetterWrapper,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* data) {
+ return Accessor(env, object, utf8name.c_str(), getter, setter, attributes, data);
+}
+
+template <typename Getter, typename Setter>
+inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env,
+ Napi::Object object,
+ Name name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes,
+ void* data) {
+ typedef details::AccessorCallbackData<Getter, Setter> CbData;
+ auto callbackData = new CbData({ getter, setter, data });
+
+ napi_status status = AttachData(env, object, callbackData);
+ if (status != napi_ok) {
+ delete callbackData;
+ NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor());
+ }
+
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ nullptr,
+ CbData::GetterWrapper,
+ CbData::SetterWrapper,
+ nullptr,
+ attributes,
+ callbackData
+ });
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env,
+ Napi::Object /*object*/,
+ const char* utf8name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* data) {
+ return PropertyDescriptor({
+ utf8name,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Napi::Function::New(env, cb, utf8name, data),
+ attributes,
+ nullptr
+ });
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* data) {
+ return Function(env, object, utf8name.c_str(), cb, attributes, data);
+}
+
+template <typename Callable>
+inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env,
+ Napi::Object /*object*/,
+ Name name,
+ Callable cb,
+ napi_property_attributes attributes,
+ void* data) {
+ return PropertyDescriptor({
+ nullptr,
+ name,
+ nullptr,
+ nullptr,
+ nullptr,
+ Napi::Function::New(env, cb, nullptr, data),
+ attributes,
+ nullptr
+ });
+}
+
+inline PropertyDescriptor PropertyDescriptor::Value(const char* utf8name,
+ napi_value value,
+ napi_property_attributes attributes) {
+ return PropertyDescriptor({
+ utf8name, nullptr, nullptr, nullptr, nullptr, value, attributes, nullptr
+ });
+}
+
+inline PropertyDescriptor PropertyDescriptor::Value(const std::string& utf8name,
+ napi_value value,
+ napi_property_attributes attributes) {
+ return Value(utf8name.c_str(), value, attributes);
+}
+
+inline PropertyDescriptor PropertyDescriptor::Value(napi_value name,
+ napi_value value,
+ napi_property_attributes attributes) {
+ return PropertyDescriptor({
+ nullptr, name, nullptr, nullptr, nullptr, value, attributes, nullptr
+ });
+}
+
+inline PropertyDescriptor PropertyDescriptor::Value(Name name,
+ Napi::Value value,
+ napi_property_attributes attributes) {
+ napi_value nameValue = name;
+ napi_value valueValue = value;
+ return PropertyDescriptor::Value(nameValue, valueValue, attributes);
+}
+
+inline PropertyDescriptor::PropertyDescriptor(napi_property_descriptor desc)
+ : _desc(desc) {
+}
+
+inline PropertyDescriptor::operator napi_property_descriptor&() {
+ return _desc;
+}
+
+inline PropertyDescriptor::operator const napi_property_descriptor&() const {
+ return _desc;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ObjectWrap<T> class
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
+ napi_env env = callbackInfo.Env();
+ napi_value wrapper = callbackInfo.This();
+ napi_status status;
+ napi_ref ref;
+ T* instance = static_cast<T*>(this);
+ status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref);
+ NAPI_THROW_IF_FAILED_VOID(env, status);
+
+ Reference<Object>* instanceRef = instance;
+ *instanceRef = Reference<Object>(env, ref);
+}
+
+template<typename T>
+inline T* ObjectWrap<T>::Unwrap(Object wrapper) {
+ T* unwrapped;
+ napi_status status = napi_unwrap(wrapper.Env(), wrapper, reinterpret_cast<void**>(&unwrapped));
+ NAPI_THROW_IF_FAILED(wrapper.Env(), status, nullptr);
+ return unwrapped;
+}
+
+template <typename T>
+inline Function
+ObjectWrap<T>::DefineClass(Napi::Env env,
+ const char* utf8name,
+ const size_t props_count,
+ const napi_property_descriptor* descriptors,
+ void* data) {
+ napi_status status;
+ std::vector<napi_property_descriptor> props(props_count);
+
+ // We copy the descriptors to a local array because before defining the class
+ // we must replace static method property descriptors with value property
+ // descriptors such that the value is a function-valued `napi_value` created
+ // with `CreateFunction()`.
+ //
+ // This replacement could be made for instance methods as well, but V8 aborts
+ // if we do that, because it expects methods defined on the prototype template
+ // to have `FunctionTemplate`s.
+ for (size_t index = 0; index < props_count; index++) {
+ props[index] = descriptors[index];
+ napi_property_descriptor* prop = &props[index];
+ if (prop->method == T::StaticMethodCallbackWrapper) {
+ status = CreateFunction(env,
+ utf8name,
+ prop->method,
+ static_cast<StaticMethodCallbackData*>(prop->data),
+ &(prop->value));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ prop->method = nullptr;
+ prop->data = nullptr;
+ } else if (prop->method == T::StaticVoidMethodCallbackWrapper) {
+ status = CreateFunction(env,
+ utf8name,
+ prop->method,
+ static_cast<StaticVoidMethodCallbackData*>(prop->data),
+ &(prop->value));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ prop->method = nullptr;
+ prop->data = nullptr;
+ }
+ }
+
+ napi_value value;
+ status = napi_define_class(env,
+ utf8name,
+ NAPI_AUTO_LENGTH,
+ T::ConstructorCallbackWrapper,
+ data,
+ props_count,
+ props.data(),
+ &value);
+ NAPI_THROW_IF_FAILED(env, status, Function());
+
+ // After defining the class we iterate once more over the property descriptors
+ // and attach the data associated with accessors and instance methods to the
+ // newly created JavaScript class.
+ for (size_t idx = 0; idx < props_count; idx++) {
+ const napi_property_descriptor* prop = &props[idx];
+
+ if (prop->getter == T::StaticGetterCallbackWrapper ||
+ prop->setter == T::StaticSetterCallbackWrapper) {
+ status = Napi::details::AttachData(env,
+ value,
+ static_cast<StaticAccessorCallbackData*>(prop->data));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ } else if (prop->getter == T::InstanceGetterCallbackWrapper ||
+ prop->setter == T::InstanceSetterCallbackWrapper) {
+ status = Napi::details::AttachData(env,
+ value,
+ static_cast<InstanceAccessorCallbackData*>(prop->data));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ } else if (prop->method != nullptr && !(prop->attributes & napi_static)) {
+ if (prop->method == T::InstanceVoidMethodCallbackWrapper) {
+ status = Napi::details::AttachData(env,
+ value,
+ static_cast<InstanceVoidMethodCallbackData*>(prop->data));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ } else if (prop->method == T::InstanceMethodCallbackWrapper) {
+ status = Napi::details::AttachData(env,
+ value,
+ static_cast<InstanceMethodCallbackData*>(prop->data));
+ NAPI_THROW_IF_FAILED(env, status, Function());
+ }
+ }
+ }
+
+ return Function(env, value);
+}
+
+template <typename T>
+inline Function ObjectWrap<T>::DefineClass(
+ Napi::Env env,
+ const char* utf8name,
+ const std::initializer_list<ClassPropertyDescriptor<T>>& properties,
+ void* data) {
+ return DefineClass(env,
+ utf8name,
+ properties.size(),
+ reinterpret_cast<const napi_property_descriptor*>(properties.begin()),
+ data);
+}
+
+template <typename T>
+inline Function ObjectWrap<T>::DefineClass(
+ Napi::Env env,
+ const char* utf8name,
+ const std::vector<ClassPropertyDescriptor<T>>& properties,
+ void* data) {
+ return DefineClass(env,
+ utf8name,
+ properties.size(),
+ reinterpret_cast<const napi_property_descriptor*>(properties.data()),
+ data);
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticMethod(
+ const char* utf8name,
+ StaticVoidMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.method = T::StaticVoidMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticMethod(
+ const char* utf8name,
+ StaticMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.method = T::StaticMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticMethod(
+ Symbol name,
+ StaticVoidMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.method = T::StaticVoidMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticMethod(
+ Symbol name,
+ StaticMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.method = T::StaticMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticAccessor(
+ const char* utf8name,
+ StaticGetterCallback getter,
+ StaticSetterCallback setter,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticAccessorCallbackData* callbackData =
+ new StaticAccessorCallbackData({ getter, setter, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr;
+ desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticAccessor(
+ Symbol name,
+ StaticGetterCallback getter,
+ StaticSetterCallback setter,
+ napi_property_attributes attributes,
+ void* data) {
+ StaticAccessorCallbackData* callbackData =
+ new StaticAccessorCallbackData({ getter, setter, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr;
+ desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr;
+ desc.data = callbackData;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceMethod(
+ const char* utf8name,
+ InstanceVoidMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceVoidMethodCallbackData* callbackData =
+ new InstanceVoidMethodCallbackData({ method, data});
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.method = T::InstanceVoidMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceMethod(
+ const char* utf8name,
+ InstanceMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.method = T::InstanceMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceMethod(
+ Symbol name,
+ InstanceVoidMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceVoidMethodCallbackData* callbackData =
+ new InstanceVoidMethodCallbackData({ method, data});
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.method = T::InstanceVoidMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceMethod(
+ Symbol name,
+ InstanceMethodCallback method,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.method = T::InstanceMethodCallbackWrapper;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceAccessor(
+ const char* utf8name,
+ InstanceGetterCallback getter,
+ InstanceSetterCallback setter,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceAccessorCallbackData* callbackData =
+ new InstanceAccessorCallbackData({ getter, setter, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr;
+ desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceAccessor(
+ Symbol name,
+ InstanceGetterCallback getter,
+ InstanceSetterCallback setter,
+ napi_property_attributes attributes,
+ void* data) {
+ InstanceAccessorCallbackData* callbackData =
+ new InstanceAccessorCallbackData({ getter, setter, data });
+
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr;
+ desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr;
+ desc.data = callbackData;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticValue(const char* utf8name,
+ Napi::Value value, napi_property_attributes attributes) {
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.value = value;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticValue(Symbol name,
+ Napi::Value value, napi_property_attributes attributes) {
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.value = value;
+ desc.attributes = static_cast<napi_property_attributes>(attributes | napi_static);
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceValue(
+ const char* utf8name,
+ Napi::Value value,
+ napi_property_attributes attributes) {
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.utf8name = utf8name;
+ desc.value = value;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceValue(
+ Symbol name,
+ Napi::Value value,
+ napi_property_attributes attributes) {
+ napi_property_descriptor desc = napi_property_descriptor();
+ desc.name = name;
+ desc.value = value;
+ desc.attributes = attributes;
+ return desc;
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ napi_value new_target;
+ napi_status status = napi_get_new_target(env, info, &new_target);
+ if (status != napi_ok) return nullptr;
+
+ bool isConstructCall = (new_target != nullptr);
+ if (!isConstructCall) {
+ napi_throw_type_error(env, nullptr, "Class constructors cannot be invoked without 'new'");
+ return nullptr;
+ }
+
+ T* instance;
+ napi_value wrapper = details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ instance = new T(callbackInfo);
+ return callbackInfo.This();
+ });
+
+ return wrapper;
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::StaticVoidMethodCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ StaticVoidMethodCallbackData* callbackData =
+ reinterpret_cast<StaticVoidMethodCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ callbackData->callback(callbackInfo);
+ return nullptr;
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::StaticMethodCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ StaticMethodCallbackData* callbackData =
+ reinterpret_cast<StaticMethodCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ return callbackData->callback(callbackInfo);
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::StaticGetterCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ StaticAccessorCallbackData* callbackData =
+ reinterpret_cast<StaticAccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ return callbackData->getterCallback(callbackInfo);
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::StaticSetterCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ StaticAccessorCallbackData* callbackData =
+ reinterpret_cast<StaticAccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ callbackData->setterCallback(callbackInfo, callbackInfo[0]);
+ return nullptr;
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::InstanceVoidMethodCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ InstanceVoidMethodCallbackData* callbackData =
+ reinterpret_cast<InstanceVoidMethodCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ T* instance = Unwrap(callbackInfo.This().As<Object>());
+ auto cb = callbackData->callback;
+ (instance->*cb)(callbackInfo);
+ return nullptr;
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::InstanceMethodCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ InstanceMethodCallbackData* callbackData =
+ reinterpret_cast<InstanceMethodCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ T* instance = Unwrap(callbackInfo.This().As<Object>());
+ auto cb = callbackData->callback;
+ return (instance->*cb)(callbackInfo);
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::InstanceGetterCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ InstanceAccessorCallbackData* callbackData =
+ reinterpret_cast<InstanceAccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ T* instance = Unwrap(callbackInfo.This().As<Object>());
+ auto cb = callbackData->getterCallback;
+ return (instance->*cb)(callbackInfo);
+ });
+}
+
+template <typename T>
+inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
+ napi_env env,
+ napi_callback_info info) {
+ return details::WrapCallback([&] {
+ CallbackInfo callbackInfo(env, info);
+ InstanceAccessorCallbackData* callbackData =
+ reinterpret_cast<InstanceAccessorCallbackData*>(callbackInfo.Data());
+ callbackInfo.SetData(callbackData->data);
+ T* instance = Unwrap(callbackInfo.This().As<Object>());
+ auto cb = callbackData->setterCallback;
+ (instance->*cb)(callbackInfo, callbackInfo[0]);
+ return nullptr;
+ });
+}
+
+template <typename T>
+inline void ObjectWrap<T>::FinalizeCallback(napi_env /*env*/, void* data, void* /*hint*/) {
+ T* instance = reinterpret_cast<T*>(data);
+ delete instance;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HandleScope class
+////////////////////////////////////////////////////////////////////////////////
+
+inline HandleScope::HandleScope(napi_env env, napi_handle_scope scope)
+ : _env(env), _scope(scope) {
+}
+
+inline HandleScope::HandleScope(Napi::Env env) : _env(env) {
+ napi_status status = napi_open_handle_scope(_env, &_scope);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline HandleScope::~HandleScope() {
+ napi_close_handle_scope(_env, _scope);
+}
+
+inline HandleScope::operator napi_handle_scope() const {
+ return _scope;
+}
+
+inline Napi::Env HandleScope::Env() const {
+ return Napi::Env(_env);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// EscapableHandleScope class
+////////////////////////////////////////////////////////////////////////////////
+
+inline EscapableHandleScope::EscapableHandleScope(
+ napi_env env, napi_escapable_handle_scope scope) : _env(env), _scope(scope) {
+}
+
+inline EscapableHandleScope::EscapableHandleScope(Napi::Env env) : _env(env) {
+ napi_status status = napi_open_escapable_handle_scope(_env, &_scope);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline EscapableHandleScope::~EscapableHandleScope() {
+ napi_close_escapable_handle_scope(_env, _scope);
+}
+
+inline EscapableHandleScope::operator napi_escapable_handle_scope() const {
+ return _scope;
+}
+
+inline Napi::Env EscapableHandleScope::Env() const {
+ return Napi::Env(_env);
+}
+
+inline Value EscapableHandleScope::Escape(napi_value escapee) {
+ napi_value result;
+ napi_status status = napi_escape_handle(_env, _scope, escapee, &result);
+ NAPI_THROW_IF_FAILED(_env, status, Value());
+ return Value(_env, result);
+}
+
+
+#if (NAPI_VERSION > 2)
+////////////////////////////////////////////////////////////////////////////////
+// CallbackScope class
+////////////////////////////////////////////////////////////////////////////////
+
+inline CallbackScope::CallbackScope(
+ napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) {
+}
+
+inline CallbackScope::CallbackScope(napi_env env, napi_async_context context)
+ : _env(env) {
+ napi_status status = napi_open_callback_scope(
+ _env, Object::New(env), context, &_scope);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline CallbackScope::~CallbackScope() {
+ napi_close_callback_scope(_env, _scope);
+}
+
+inline CallbackScope::operator napi_callback_scope() const {
+ return _scope;
+}
+
+inline Napi::Env CallbackScope::Env() const {
+ return Napi::Env(_env);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// AsyncContext class
+////////////////////////////////////////////////////////////////////////////////
+
+inline AsyncContext::AsyncContext(napi_env env, const char* resource_name)
+ : AsyncContext(env, resource_name, Object::New(env)) {
+}
+
+inline AsyncContext::AsyncContext(napi_env env,
+ const char* resource_name,
+ const Object& resource)
+ : _env(env),
+ _context(nullptr) {
+ napi_value resource_id;
+ napi_status status = napi_create_string_utf8(
+ _env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+
+ status = napi_async_init(_env, resource, resource_id, &_context);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline AsyncContext::~AsyncContext() {
+ if (_context != nullptr) {
+ napi_async_destroy(_env, _context);
+ _context = nullptr;
+ }
+}
+
+inline AsyncContext::AsyncContext(AsyncContext&& other) {
+ _env = other._env;
+ other._env = nullptr;
+ _context = other._context;
+ other._context = nullptr;
+}
+
+inline AsyncContext& AsyncContext::operator =(AsyncContext&& other) {
+ _env = other._env;
+ other._env = nullptr;
+ _context = other._context;
+ other._context = nullptr;
+ return *this;
+}
+
+inline AsyncContext::operator napi_async_context() const {
+ return _context;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AsyncWorker class
+////////////////////////////////////////////////////////////////////////////////
+
+inline AsyncWorker::AsyncWorker(const Function& callback)
+ : AsyncWorker(callback, "generic") {
+}
+
+inline AsyncWorker::AsyncWorker(const Function& callback,
+ const char* resource_name)
+ : AsyncWorker(callback, resource_name, Object::New(callback.Env())) {
+}
+
+inline AsyncWorker::AsyncWorker(const Function& callback,
+ const char* resource_name,
+ const Object& resource)
+ : AsyncWorker(Object::New(callback.Env()),
+ callback,
+ resource_name,
+ resource) {
+}
+
+inline AsyncWorker::AsyncWorker(const Object& receiver,
+ const Function& callback)
+ : AsyncWorker(receiver, callback, "generic") {
+}
+
+inline AsyncWorker::AsyncWorker(const Object& receiver,
+ const Function& callback,
+ const char* resource_name)
+ : AsyncWorker(receiver,
+ callback,
+ resource_name,
+ Object::New(callback.Env())) {
+}
+
+inline AsyncWorker::AsyncWorker(const Object& receiver,
+ const Function& callback,
+ const char* resource_name,
+ const Object& resource)
+ : _env(callback.Env()),
+ _receiver(Napi::Persistent(receiver)),
+ _callback(Napi::Persistent(callback)),
+ _suppress_destruct(false) {
+ napi_value resource_id;
+ napi_status status = napi_create_string_latin1(
+ _env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+
+ status = napi_create_async_work(_env, resource, resource_id, OnExecute,
+ OnWorkComplete, this, &_work);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline AsyncWorker::AsyncWorker(Napi::Env env)
+ : AsyncWorker(env, "generic") {
+}
+
+inline AsyncWorker::AsyncWorker(Napi::Env env,
+ const char* resource_name)
+ : AsyncWorker(env, resource_name, Object::New(env)) {
+}
+
+inline AsyncWorker::AsyncWorker(Napi::Env env,
+ const char* resource_name,
+ const Object& resource)
+ : _env(env),
+ _receiver(),
+ _callback(),
+ _suppress_destruct(false) {
+ napi_value resource_id;
+ napi_status status = napi_create_string_latin1(
+ _env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+
+ status = napi_create_async_work(_env, resource, resource_id, OnExecute,
+ OnWorkComplete, this, &_work);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline AsyncWorker::~AsyncWorker() {
+ if (_work != nullptr) {
+ napi_delete_async_work(_env, _work);
+ _work = nullptr;
+ }
+}
+
+inline void AsyncWorker::Destroy() {
+ delete this;
+}
+
+inline AsyncWorker::AsyncWorker(AsyncWorker&& other) {
+ _env = other._env;
+ other._env = nullptr;
+ _work = other._work;
+ other._work = nullptr;
+ _receiver = std::move(other._receiver);
+ _callback = std::move(other._callback);
+ _error = std::move(other._error);
+ _suppress_destruct = other._suppress_destruct;
+}
+
+inline AsyncWorker& AsyncWorker::operator =(AsyncWorker&& other) {
+ _env = other._env;
+ other._env = nullptr;
+ _work = other._work;
+ other._work = nullptr;
+ _receiver = std::move(other._receiver);
+ _callback = std::move(other._callback);
+ _error = std::move(other._error);
+ _suppress_destruct = other._suppress_destruct;
+ return *this;
+}
+
+inline AsyncWorker::operator napi_async_work() const {
+ return _work;
+}
+
+inline Napi::Env AsyncWorker::Env() const {
+ return Napi::Env(_env);
+}
+
+inline void AsyncWorker::Queue() {
+ napi_status status = napi_queue_async_work(_env, _work);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline void AsyncWorker::Cancel() {
+ napi_status status = napi_cancel_async_work(_env, _work);
+ NAPI_THROW_IF_FAILED_VOID(_env, status);
+}
+
+inline ObjectReference& AsyncWorker::Receiver() {
+ return _receiver;
+}
+
+inline FunctionReference& AsyncWorker::Callback() {
+ return _callback;
+}
+
+inline void AsyncWorker::SuppressDestruct() {
+ _suppress_destruct = true;
+}
+
+inline void AsyncWorker::OnOK() {
+ if (!_callback.IsEmpty()) {
+ _callback.Call(_receiver.Value(), GetResult(_callback.Env()));
+ }
+}
+
+inline void AsyncWorker::OnError(const Error& e) {
+ if (!_callback.IsEmpty()) {
+ _callback.Call(_receiver.Value(), std::initializer_list<napi_value>{ e.Value() });
+ }
+}
+
+inline void AsyncWorker::SetError(const std::string& error) {
+ _error = error;
+}
+
+inline std::vector<napi_value> AsyncWorker::GetResult(Napi::Env /*env*/) {
+ return {};
+}
+// The OnExecute method receives an napi_env argument. However, do NOT
+// use it within this method, as it does not run on the main thread and must
+// not run any method that would cause JavaScript to run. In practice, this
+// means that almost any use of napi_env will be incorrect.
+inline void AsyncWorker::OnExecute(napi_env /*DO_NOT_USE*/, void* this_pointer) {
+ AsyncWorker* self = static_cast<AsyncWorker*>(this_pointer);
+#ifdef NAPI_CPP_EXCEPTIONS
+ try {
+ self->Execute();
+ } catch (const std::exception& e) {
+ self->SetError(e.what());
+ }
+#else // NAPI_CPP_EXCEPTIONS
+ self->Execute();
+#endif // NAPI_CPP_EXCEPTIONS
+}
+
+inline void AsyncWorker::OnWorkComplete(
+ napi_env /*env*/, napi_status status, void* this_pointer) {
+ AsyncWorker* self = static_cast<AsyncWorker*>(this_pointer);
+ if (status != napi_cancelled) {
+ HandleScope scope(self->_env);
+ details::WrapCallback([&] {
+ if (self->_error.size() == 0) {
+ self->OnOK();
+ }
+ else {
+ self->OnError(Error::New(self->_env, self->_error));
+ }
+ return nullptr;
+ });
+ }
+ if (!self->_suppress_destruct) {
+ self->Destroy();
+ }
+}
+
+#if (NAPI_VERSION > 3)
+////////////////////////////////////////////////////////////////////////////////
+// ThreadSafeFunction class
+////////////////////////////////////////////////////////////////////////////////
+
+// static
+template <typename ResourceString>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount);
+}
+
+// static
+template <typename ResourceString, typename ContextType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount, context);
+}
+
+// static
+template <typename ResourceString, typename Finalizer>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount, finalizeCallback);
+}
+
+// static
+template <typename ResourceString, typename Finalizer,
+ typename FinalizerDataType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount, finalizeCallback, data);
+}
+
+// static
+template <typename ResourceString, typename ContextType, typename Finalizer>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount, context, finalizeCallback);
+}
+
+// static
+template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data) {
+ return New(env, callback, Object(), resourceName, maxQueueSize,
+ initialThreadCount, context, finalizeCallback, data);
+}
+
+// static
+template <typename ResourceString>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, static_cast<void*>(nullptr) /* context */);
+}
+
+// static
+template <typename ResourceString, typename ContextType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, context,
+ [](Env, ContextType*) {} /* empty finalizer */);
+}
+
+// static
+template <typename ResourceString, typename Finalizer>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, static_cast<void*>(nullptr) /* context */,
+ finalizeCallback, static_cast<void*>(nullptr) /* data */,
+ details::ThreadSafeFinalize<void, Finalizer>::Wrapper);
+}
+
+// static
+template <typename ResourceString, typename Finalizer,
+ typename FinalizerDataType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, static_cast<void*>(nullptr) /* context */,
+ finalizeCallback, data,
+ details::ThreadSafeFinalize<
+ void, Finalizer, FinalizerDataType>::FinalizeWrapperWithData);
+}
+
+// static
+template <typename ResourceString, typename ContextType, typename Finalizer>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, context, finalizeCallback,
+ static_cast<void*>(nullptr) /* data */,
+ details::ThreadSafeFinalize<
+ ContextType, Finalizer>::FinalizeWrapperWithContext);
+}
+
+// static
+template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data) {
+ return New(env, callback, resource, resourceName, maxQueueSize,
+ initialThreadCount, context, finalizeCallback, data,
+ details::ThreadSafeFinalize<ContextType, Finalizer,
+ FinalizerDataType>::FinalizeFinalizeWrapperWithDataAndContext);
+}
+
+inline ThreadSafeFunction::ThreadSafeFunction()
+ : _tsfn(new napi_threadsafe_function(nullptr)) {
+}
+
+inline ThreadSafeFunction::ThreadSafeFunction(
+ napi_threadsafe_function tsfn)
+ : _tsfn(new napi_threadsafe_function(tsfn)) {
+}
+
+inline ThreadSafeFunction::ThreadSafeFunction(ThreadSafeFunction&& other)
+ : _tsfn(std::move(other._tsfn)) {
+ other._tsfn.reset();
+}
+
+inline ThreadSafeFunction& ThreadSafeFunction::operator =(
+ ThreadSafeFunction&& other) {
+ if (*_tsfn != nullptr) {
+ Error::Fatal("ThreadSafeFunction::operator =",
+ "You cannot assign a new TSFN because existing one is still alive.");
+ return *this;
+ }
+ _tsfn = std::move(other._tsfn);
+ other._tsfn.reset();
+ return *this;
+}
+
+inline napi_status ThreadSafeFunction::BlockingCall() const {
+ return CallInternal(nullptr, napi_tsfn_blocking);
+}
+
+template <typename Callback>
+inline napi_status ThreadSafeFunction::BlockingCall(
+ Callback callback) const {
+ return CallInternal(new CallbackWrapper(callback), napi_tsfn_blocking);
+}
+
+template <typename DataType, typename Callback>
+inline napi_status ThreadSafeFunction::BlockingCall(
+ DataType* data, Callback callback) const {
+ auto wrapper = [data, callback](Env env, Function jsCallback) {
+ callback(env, jsCallback, data);
+ };
+ return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_blocking);
+}
+
+inline napi_status ThreadSafeFunction::NonBlockingCall() const {
+ return CallInternal(nullptr, napi_tsfn_nonblocking);
+}
+
+template <typename Callback>
+inline napi_status ThreadSafeFunction::NonBlockingCall(
+ Callback callback) const {
+ return CallInternal(new CallbackWrapper(callback), napi_tsfn_nonblocking);
+}
+
+template <typename DataType, typename Callback>
+inline napi_status ThreadSafeFunction::NonBlockingCall(
+ DataType* data, Callback callback) const {
+ auto wrapper = [data, callback](Env env, Function jsCallback) {
+ callback(env, jsCallback, data);
+ };
+ return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_nonblocking);
+}
+
+inline napi_status ThreadSafeFunction::Acquire() const {
+ return napi_acquire_threadsafe_function(*_tsfn);
+}
+
+inline napi_status ThreadSafeFunction::Release() {
+ return napi_release_threadsafe_function(*_tsfn, napi_tsfn_release);
+}
+
+inline napi_status ThreadSafeFunction::Abort() {
+ return napi_release_threadsafe_function(*_tsfn, napi_tsfn_abort);
+}
+
+inline ThreadSafeFunction::ConvertibleContext
+ThreadSafeFunction::GetContext() const {
+ void* context;
+ napi_get_threadsafe_function_context(*_tsfn, &context);
+ return ConvertibleContext({ context });
+}
+
+// static
+template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data,
+ napi_finalize wrapper) {
+ static_assert(details::can_make_string<ResourceString>::value
+ || std::is_convertible<ResourceString, napi_value>::value,
+ "Resource name should be convertible to the string type");
+
+ ThreadSafeFunction tsfn;
+ auto* finalizeData = new details::ThreadSafeFinalize<ContextType, Finalizer,
+ FinalizerDataType>({ data, finalizeCallback, tsfn._tsfn.get() });
+ napi_status status = napi_create_threadsafe_function(env, callback, resource,
+ Value::From(env, resourceName), maxQueueSize, initialThreadCount,
+ finalizeData, wrapper, context, CallJS, tsfn._tsfn.get());
+ if (status != napi_ok) {
+ delete finalizeData;
+ NAPI_THROW_IF_FAILED(env, status, ThreadSafeFunction());
+ }
+
+ return tsfn;
+}
+
+inline napi_status ThreadSafeFunction::CallInternal(
+ CallbackWrapper* callbackWrapper,
+ napi_threadsafe_function_call_mode mode) const {
+ napi_status status = napi_call_threadsafe_function(
+ *_tsfn, callbackWrapper, mode);
+ if (status != napi_ok && callbackWrapper != nullptr) {
+ delete callbackWrapper;
+ }
+
+ return status;
+}
+
+// static
+inline void ThreadSafeFunction::CallJS(napi_env env,
+ napi_value jsCallback,
+ void* /* context */,
+ void* data) {
+ if (env == nullptr && jsCallback == nullptr) {
+ return;
+ }
+
+ if (data != nullptr) {
+ auto* callbackWrapper = static_cast<CallbackWrapper*>(data);
+ (*callbackWrapper)(env, Function(env, jsCallback));
+ delete callbackWrapper;
+ } else if (jsCallback != nullptr) {
+ Function(env, jsCallback).Call({});
+ }
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Memory Management class
+////////////////////////////////////////////////////////////////////////////////
+
+inline int64_t MemoryManagement::AdjustExternalMemory(Env env, int64_t change_in_bytes) {
+ int64_t result;
+ napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result);
+ NAPI_THROW_IF_FAILED(env, status, 0);
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Version Management class
+////////////////////////////////////////////////////////////////////////////////
+
+inline uint32_t VersionManagement::GetNapiVersion(Env env) {
+ uint32_t result;
+ napi_status status = napi_get_version(env, &result);
+ NAPI_THROW_IF_FAILED(env, status, 0);
+ return result;
+}
+
+inline const napi_node_version* VersionManagement::GetNodeVersion(Env env) {
+ const napi_node_version* result;
+ napi_status status = napi_get_node_version(env, &result);
+ NAPI_THROW_IF_FAILED(env, status, 0);
+ return result;
+}
+
+} // namespace Napi
+
+#endif // SRC_NAPI_INL_H_
new file mode 100644
@@ -0,0 +1,2053 @@
+#ifndef SRC_NAPI_H_
+#define SRC_NAPI_H_
+
+#include <node_api.h>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <string>
+#include <vector>
+
+// VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version)
+#if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210
+#define NAPI_HAS_CONSTEXPR 1
+#endif
+
+// VS2013 does not support char16_t literal strings, so we'll work around it using wchar_t strings
+// and casting them. This is safe as long as the character sizes are the same.
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t");
+#define NAPI_WIDE_TEXT(x) reinterpret_cast<char16_t*>(L ## x)
+#else
+#define NAPI_WIDE_TEXT(x) u ## x
+#endif
+
+// If C++ exceptions are not explicitly enabled or disabled, enable them
+// if exceptions were enabled in the compiler settings.
+#if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS)
+ #if defined(_CPPUNWIND) || defined (__EXCEPTIONS)
+ #define NAPI_CPP_EXCEPTIONS
+ #else
+ #error Exception support not detected. \
+ Define either NAPI_CPP_EXCEPTIONS or NAPI_DISABLE_CPP_EXCEPTIONS.
+ #endif
+#endif
+
+#ifdef _NOEXCEPT
+ #define NAPI_NOEXCEPT _NOEXCEPT
+#else
+ #define NAPI_NOEXCEPT noexcept
+#endif
+
+#ifdef NAPI_CPP_EXCEPTIONS
+
+// When C++ exceptions are enabled, Errors are thrown directly. There is no need
+// to return anything after the throw statements. The variadic parameter is an
+// optional return value that is ignored.
+// We need _VOID versions of the macros to avoid warnings resulting from
+// leaving the NAPI_THROW_* `...` argument empty.
+
+#define NAPI_THROW(e, ...) throw e
+#define NAPI_THROW_VOID(e) throw e
+
+#define NAPI_THROW_IF_FAILED(env, status, ...) \
+ if ((status) != napi_ok) throw Napi::Error::New(env);
+
+#define NAPI_THROW_IF_FAILED_VOID(env, status) \
+ if ((status) != napi_ok) throw Napi::Error::New(env);
+
+#else // NAPI_CPP_EXCEPTIONS
+
+// When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions,
+// which are pending until the callback returns to JS. The variadic parameter
+// is an optional return value; usually it is an empty result.
+// We need _VOID versions of the macros to avoid warnings resulting from
+// leaving the NAPI_THROW_* `...` argument empty.
+
+#define NAPI_THROW(e, ...) \
+ do { \
+ (e).ThrowAsJavaScriptException(); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define NAPI_THROW_VOID(e) \
+ do { \
+ (e).ThrowAsJavaScriptException(); \
+ return; \
+ } while (0)
+
+#define NAPI_THROW_IF_FAILED(env, status, ...) \
+ if ((status) != napi_ok) { \
+ Napi::Error::New(env).ThrowAsJavaScriptException(); \
+ return __VA_ARGS__; \
+ }
+
+#define NAPI_THROW_IF_FAILED_VOID(env, status) \
+ if ((status) != napi_ok) { \
+ Napi::Error::New(env).ThrowAsJavaScriptException(); \
+ return; \
+ }
+
+#endif // NAPI_CPP_EXCEPTIONS
+
+#define NAPI_FATAL_IF_FAILED(status, location, message) \
+ do { \
+ if ((status) != napi_ok) { \
+ Napi::Error::Fatal((location), (message)); \
+ } \
+ } while (0)
+
+////////////////////////////////////////////////////////////////////////////////
+/// N-API C++ Wrapper Classes
+///
+/// These classes wrap the "N-API" ABI-stable C APIs for Node.js, providing a
+/// C++ object model and C++ exception-handling semantics with low overhead.
+/// The wrappers are all header-only so that they do not affect the ABI.
+////////////////////////////////////////////////////////////////////////////////
+namespace Napi {
+
+ // Forward declarations
+ class Env;
+ class Value;
+ class Boolean;
+ class Number;
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+ class BigInt;
+#endif // NAPI_EXPERIMENTAL
+ class String;
+ class Object;
+ class Array;
+ class Function;
+ template <typename T> class Buffer;
+ class Error;
+ class PropertyDescriptor;
+ class CallbackInfo;
+ template <typename T> class Reference;
+ class TypedArray;
+ template <typename T> class TypedArrayOf;
+
+ typedef TypedArrayOf<int8_t> Int8Array; ///< Typed-array of signed 8-bit integers
+ typedef TypedArrayOf<uint8_t> Uint8Array; ///< Typed-array of unsigned 8-bit integers
+ typedef TypedArrayOf<int16_t> Int16Array; ///< Typed-array of signed 16-bit integers
+ typedef TypedArrayOf<uint16_t> Uint16Array; ///< Typed-array of unsigned 16-bit integers
+ typedef TypedArrayOf<int32_t> Int32Array; ///< Typed-array of signed 32-bit integers
+ typedef TypedArrayOf<uint32_t> Uint32Array; ///< Typed-array of unsigned 32-bit integers
+ typedef TypedArrayOf<float> Float32Array; ///< Typed-array of 32-bit floating-point values
+ typedef TypedArrayOf<double> Float64Array; ///< Typed-array of 64-bit floating-point values
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+ typedef TypedArrayOf<int64_t> BigInt64Array; ///< Typed array of signed 64-bit integers
+ typedef TypedArrayOf<uint64_t> BigUint64Array; ///< Typed array of unsigned 64-bit integers
+#endif // NAPI_EXPERIMENTAL
+
+ /// Defines the signature of a N-API C++ module's registration callback (init) function.
+ typedef Object (*ModuleRegisterCallback)(Env env, Object exports);
+
+ class MemoryManagement;
+
+ /// Environment for N-API values and operations.
+ ///
+ /// All N-API values and operations must be associated with an environment. An environment
+ /// instance is always provided to callback functions; that environment must then be used for any
+ /// creation of N-API values or other N-API operations within the callback. (Many methods infer
+ /// the environment from the `this` instance that the method is called on.)
+ ///
+ /// In the future, multiple environments per process may be supported, although current
+ /// implementations only support one environment per process.
+ ///
+ /// In the V8 JavaScript engine, a N-API environment approximately corresponds to an Isolate.
+ class Env {
+ public:
+ Env(napi_env env);
+
+ operator napi_env() const;
+
+ Object Global() const;
+ Value Undefined() const;
+ Value Null() const;
+
+ bool IsExceptionPending() const;
+ Error GetAndClearPendingException();
+
+ private:
+ napi_env _env;
+ };
+
+ /// A JavaScript value of unknown type.
+ ///
+ /// For type-specific operations, convert to one of the Value subclasses using a `To*` or `As()`
+ /// method. The `To*` methods do type coercion; the `As()` method does not.
+ ///
+ /// Napi::Value value = ...
+ /// if (!value.IsString()) throw Napi::TypeError::New(env, "Invalid arg...");
+ /// Napi::String str = value.As<Napi::String>(); // Cast to a string value
+ ///
+ /// Napi::Value anotherValue = ...
+ /// bool isTruthy = anotherValue.ToBoolean(); // Coerce to a boolean value
+ class Value {
+ public:
+ Value(); ///< Creates a new _empty_ Value instance.
+ Value(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ /// Creates a JS value from a C++ primitive.
+ ///
+ /// `value` may be any of:
+ /// - bool
+ /// - Any integer type
+ /// - Any floating point type
+ /// - const char* (encoded using UTF-8, null-terminated)
+ /// - const char16_t* (encoded using UTF-16-LE, null-terminated)
+ /// - std::string (encoded using UTF-8)
+ /// - std::u16string
+ /// - napi::Value
+ /// - napi_value
+ template <typename T>
+ static Value From(napi_env env, const T& value);
+
+ /// Converts to a N-API value primitive.
+ ///
+ /// If the instance is _empty_, this returns `nullptr`.
+ operator napi_value() const;
+
+ /// Tests if this value strictly equals another value.
+ bool operator ==(const Value& other) const;
+
+ /// Tests if this value does not strictly equal another value.
+ bool operator !=(const Value& other) const;
+
+ /// Tests if this value strictly equals another value.
+ bool StrictEquals(const Value& other) const;
+
+ /// Gets the environment the value is associated with.
+ Napi::Env Env() const;
+
+ /// Checks if the value is empty (uninitialized).
+ ///
+ /// An empty value is invalid, and most attempts to perform an operation on an empty value
+ /// will result in an exception. Note an empty value is distinct from JavaScript `null` or
+ /// `undefined`, which are valid values.
+ ///
+ /// When C++ exceptions are disabled at compile time, a method with a `Value` return type may
+ /// return an empty value to indicate a pending exception. So when not using C++ exceptions,
+ /// callers should check whether the value is empty before attempting to use it.
+ bool IsEmpty() const;
+
+ napi_valuetype Type() const; ///< Gets the type of the value.
+
+ bool IsUndefined() const; ///< Tests if a value is an undefined JavaScript value.
+ bool IsNull() const; ///< Tests if a value is a null JavaScript value.
+ bool IsBoolean() const; ///< Tests if a value is a JavaScript boolean.
+ bool IsNumber() const; ///< Tests if a value is a JavaScript number.
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+ bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint.
+#endif // NAPI_EXPERIMENTAL
+ bool IsString() const; ///< Tests if a value is a JavaScript string.
+ bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol.
+ bool IsArray() const; ///< Tests if a value is a JavaScript array.
+ bool IsArrayBuffer() const; ///< Tests if a value is a JavaScript array buffer.
+ bool IsTypedArray() const; ///< Tests if a value is a JavaScript typed array.
+ bool IsObject() const; ///< Tests if a value is a JavaScript object.
+ bool IsFunction() const; ///< Tests if a value is a JavaScript function.
+ bool IsPromise() const; ///< Tests if a value is a JavaScript promise.
+ bool IsDataView() const; ///< Tests if a value is a JavaScript data view.
+ bool IsBuffer() const; ///< Tests if a value is a Node buffer.
+ bool IsExternal() const; ///< Tests if a value is a pointer to external data.
+
+ /// Casts to another type of `Napi::Value`, when the actual type is known or assumed.
+ ///
+ /// This conversion does NOT coerce the type. Calling any methods inappropriate for the actual
+ /// value type will throw `Napi::Error`.
+ template <typename T> T As() const;
+
+ Boolean ToBoolean() const; ///< Coerces a value to a JavaScript boolean.
+ Number ToNumber() const; ///< Coerces a value to a JavaScript number.
+ String ToString() const; ///< Coerces a value to a JavaScript string.
+ Object ToObject() const; ///< Coerces a value to a JavaScript object.
+
+ protected:
+ /// !cond INTERNAL
+ napi_env _env;
+ napi_value _value;
+ /// !endcond
+ };
+
+ /// A JavaScript boolean value.
+ class Boolean : public Value {
+ public:
+ static Boolean New(
+ napi_env env, ///< N-API environment
+ bool value ///< Boolean value
+ );
+
+ Boolean(); ///< Creates a new _empty_ Boolean instance.
+ Boolean(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ operator bool() const; ///< Converts a Boolean value to a boolean primitive.
+ bool Value() const; ///< Converts a Boolean value to a boolean primitive.
+ };
+
+ /// A JavaScript number value.
+ class Number : public Value {
+ public:
+ static Number New(
+ napi_env env, ///< N-API environment
+ double value ///< Number value
+ );
+
+ Number(); ///< Creates a new _empty_ Number instance.
+ Number(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ operator int32_t() const; ///< Converts a Number value to a 32-bit signed integer value.
+ operator uint32_t() const; ///< Converts a Number value to a 32-bit unsigned integer value.
+ operator int64_t() const; ///< Converts a Number value to a 64-bit signed integer value.
+ operator float() const; ///< Converts a Number value to a 32-bit floating-point value.
+ operator double() const; ///< Converts a Number value to a 64-bit floating-point value.
+
+ int32_t Int32Value() const; ///< Converts a Number value to a 32-bit signed integer value.
+ uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value.
+ int64_t Int64Value() const; ///< Converts a Number value to a 64-bit signed integer value.
+ float FloatValue() const; ///< Converts a Number value to a 32-bit floating-point value.
+ double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value.
+ };
+
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+ /// A JavaScript bigint value.
+ class BigInt : public Value {
+ public:
+ static BigInt New(
+ napi_env env, ///< N-API environment
+ int64_t value ///< Number value
+ );
+ static BigInt New(
+ napi_env env, ///< N-API environment
+ uint64_t value ///< Number value
+ );
+
+ /// Creates a new BigInt object using a specified sign bit and a
+ /// specified list of digits/words.
+ /// The resulting number is calculated as:
+ /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...)
+ static BigInt New(
+ napi_env env, ///< N-API environment
+ int sign_bit, ///< Sign bit. 1 if negative.
+ size_t word_count, ///< Number of words in array
+ const uint64_t* words ///< Array of words
+ );
+
+ BigInt(); ///< Creates a new _empty_ BigInt instance.
+ BigInt(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value.
+ uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value.
+
+ size_t WordCount() const; ///< The number of 64-bit words needed to store the result of ToWords().
+
+ /// Writes the contents of this BigInt to a specified memory location.
+ /// `sign_bit` must be provided and will be set to 1 if this BigInt is negative.
+ /// `*word_count` has to be initialized to the length of the `words` array.
+ /// Upon return, it will be set to the actual number of words that would
+ /// be needed to store this BigInt (i.e. the return value of `WordCount()`).
+ void ToWords(int* sign_bit, size_t* word_count, uint64_t* words);
+ };
+#endif // NAPI_EXPERIMENTAL
+
+ /// A JavaScript string or symbol value (that can be used as a property name).
+ class Name : public Value {
+ public:
+ Name(); ///< Creates a new _empty_ Name instance.
+ Name(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+ };
+
+ /// A JavaScript string value.
+ class String : public Name {
+ public:
+ /// Creates a new String value from a UTF-8 encoded C++ string.
+ static String New(
+ napi_env env, ///< N-API environment
+ const std::string& value ///< UTF-8 encoded C++ string
+ );
+
+ /// Creates a new String value from a UTF-16 encoded C++ string.
+ static String New(
+ napi_env env, ///< N-API environment
+ const std::u16string& value ///< UTF-16 encoded C++ string
+ );
+
+ /// Creates a new String value from a UTF-8 encoded C string.
+ static String New(
+ napi_env env, ///< N-API environment
+ const char* value ///< UTF-8 encoded null-terminated C string
+ );
+
+ /// Creates a new String value from a UTF-16 encoded C string.
+ static String New(
+ napi_env env, ///< N-API environment
+ const char16_t* value ///< UTF-16 encoded null-terminated C string
+ );
+
+ /// Creates a new String value from a UTF-8 encoded C string with specified length.
+ static String New(
+ napi_env env, ///< N-API environment
+ const char* value, ///< UTF-8 encoded C string (not necessarily null-terminated)
+ size_t length ///< length of the string in bytes
+ );
+
+ /// Creates a new String value from a UTF-16 encoded C string with specified length.
+ static String New(
+ napi_env env, ///< N-API environment
+ const char16_t* value, ///< UTF-16 encoded C string (not necessarily null-terminated)
+ size_t length ///< Length of the string in 2-byte code units
+ );
+
+ /// Creates a new String based on the original object's type.
+ ///
+ /// `value` may be any of:
+ /// - const char* (encoded using UTF-8, null-terminated)
+ /// - const char16_t* (encoded using UTF-16-LE, null-terminated)
+ /// - std::string (encoded using UTF-8)
+ /// - std::u16string
+ template <typename T>
+ static String From(napi_env env, const T& value);
+
+ String(); ///< Creates a new _empty_ String instance.
+ String(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ operator std::string() const; ///< Converts a String value to a UTF-8 encoded C++ string.
+ operator std::u16string() const; ///< Converts a String value to a UTF-16 encoded C++ string.
+ std::string Utf8Value() const; ///< Converts a String value to a UTF-8 encoded C++ string.
+ std::u16string Utf16Value() const; ///< Converts a String value to a UTF-16 encoded C++ string.
+ };
+
+ /// A JavaScript symbol value.
+ class Symbol : public Name {
+ public:
+ /// Creates a new Symbol value with an optional description.
+ static Symbol New(
+ napi_env env, ///< N-API environment
+ const char* description = nullptr ///< Optional UTF-8 encoded null-terminated C string
+ /// describing the symbol
+ );
+
+ /// Creates a new Symbol value with a description.
+ static Symbol New(
+ napi_env env, ///< N-API environment
+ const std::string& description ///< UTF-8 encoded C++ string describing the symbol
+ );
+
+ /// Creates a new Symbol value with a description.
+ static Symbol New(
+ napi_env env, ///< N-API environment
+ String description ///< String value describing the symbol
+ );
+
+ /// Creates a new Symbol value with a description.
+ static Symbol New(
+ napi_env env, ///< N-API environment
+ napi_value description ///< String value describing the symbol
+ );
+
+ /// Get a public Symbol (e.g. Symbol.iterator).
+ static Symbol WellKnown(napi_env, const std::string& name);
+
+ Symbol(); ///< Creates a new _empty_ Symbol instance.
+ Symbol(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+ };
+
+ /// A JavaScript object value.
+ class Object : public Value {
+ public:
+ /// Enables property and element assignments using indexing syntax.
+ ///
+ /// Example:
+ ///
+ /// Napi::Value propertyValue = object1['A'];
+ /// object2['A'] = propertyValue;
+ /// Napi::Value elementValue = array[0];
+ /// array[1] = elementValue;
+ template <typename Key>
+ class PropertyLValue {
+ public:
+ /// Converts an L-value to a value.
+ operator Value() const;
+
+ /// Assigns a value to the property. The type of value can be
+ /// anything supported by `Object::Set`.
+ template <typename ValueType>
+ PropertyLValue& operator =(ValueType value);
+
+ private:
+ PropertyLValue() = delete;
+ PropertyLValue(Object object, Key key);
+ napi_env _env;
+ napi_value _object;
+ Key _key;
+
+ friend class Napi::Object;
+ };
+
+ /// Creates a new Object value.
+ static Object New(
+ napi_env env ///< N-API environment
+ );
+
+ Object(); ///< Creates a new _empty_ Object instance.
+ Object(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ /// Gets or sets a named property.
+ PropertyLValue<std::string> operator [](
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ );
+
+ /// Gets or sets a named property.
+ PropertyLValue<std::string> operator [](
+ const std::string& utf8name ///< UTF-8 encoded property name
+ );
+
+ /// Gets or sets an indexed property or array element.
+ PropertyLValue<uint32_t> operator [](
+ uint32_t index /// Property / element index
+ );
+
+ /// Gets a named property.
+ Value operator [](
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ ) const;
+
+ /// Gets a named property.
+ Value operator [](
+ const std::string& utf8name ///< UTF-8 encoded property name
+ ) const;
+
+ /// Gets an indexed property or array element.
+ Value operator [](
+ uint32_t index ///< Property / element index
+ ) const;
+
+ /// Checks whether a property is present.
+ bool Has(
+ napi_value key ///< Property key primitive
+ ) const;
+
+ /// Checks whether a property is present.
+ bool Has(
+ Value key ///< Property key
+ ) const;
+
+ /// Checks whether a named property is present.
+ bool Has(
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ ) const;
+
+ /// Checks whether a named property is present.
+ bool Has(
+ const std::string& utf8name ///< UTF-8 encoded property name
+ ) const;
+
+ /// Checks whether a own property is present.
+ bool HasOwnProperty(
+ napi_value key ///< Property key primitive
+ ) const;
+
+ /// Checks whether a own property is present.
+ bool HasOwnProperty(
+ Value key ///< Property key
+ ) const;
+
+ /// Checks whether a own property is present.
+ bool HasOwnProperty(
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ ) const;
+
+ /// Checks whether a own property is present.
+ bool HasOwnProperty(
+ const std::string& utf8name ///< UTF-8 encoded property name
+ ) const;
+
+ /// Gets a property.
+ Value Get(
+ napi_value key ///< Property key primitive
+ ) const;
+
+ /// Gets a property.
+ Value Get(
+ Value key ///< Property key
+ ) const;
+
+ /// Gets a named property.
+ Value Get(
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ ) const;
+
+ /// Gets a named property.
+ Value Get(
+ const std::string& utf8name ///< UTF-8 encoded property name
+ ) const;
+
+ /// Sets a property.
+ template <typename ValueType>
+ void Set(
+ napi_value key, ///< Property key primitive
+ const ValueType& value ///< Property value primitive
+ );
+
+ /// Sets a property.
+ template <typename ValueType>
+ void Set(
+ Value key, ///< Property key
+ const ValueType& value ///< Property value
+ );
+
+ /// Sets a named property.
+ template <typename ValueType>
+ void Set(
+ const char* utf8name, ///< UTF-8 encoded null-terminated property name
+ const ValueType& value
+ );
+
+ /// Sets a named property.
+ template <typename ValueType>
+ void Set(
+ const std::string& utf8name, ///< UTF-8 encoded property name
+ const ValueType& value ///< Property value primitive
+ );
+
+ /// Delete property.
+ bool Delete(
+ napi_value key ///< Property key primitive
+ );
+
+ /// Delete property.
+ bool Delete(
+ Value key ///< Property key
+ );
+
+ /// Delete property.
+ bool Delete(
+ const char* utf8name ///< UTF-8 encoded null-terminated property name
+ );
+
+ /// Delete property.
+ bool Delete(
+ const std::string& utf8name ///< UTF-8 encoded property name
+ );
+
+ /// Checks whether an indexed property is present.
+ bool Has(
+ uint32_t index ///< Property / element index
+ ) const;
+
+ /// Gets an indexed property or array element.
+ Value Get(
+ uint32_t index ///< Property / element index
+ ) const;
+
+ /// Sets an indexed property or array element.
+ template <typename ValueType>
+ void Set(
+ uint32_t index, ///< Property / element index
+ const ValueType& value ///< Property value primitive
+ );
+
+ /// Deletes an indexed property or array element.
+ bool Delete(
+ uint32_t index ///< Property / element index
+ );
+
+ Array GetPropertyNames() const; ///< Get all property names
+
+ /// Defines a property on the object.
+ void DefineProperty(
+ const PropertyDescriptor& property ///< Descriptor for the property to be defined
+ );
+
+ /// Defines properties on the object.
+ void DefineProperties(
+ const std::initializer_list<PropertyDescriptor>& properties
+ ///< List of descriptors for the properties to be defined
+ );
+
+ /// Defines properties on the object.
+ void DefineProperties(
+ const std::vector<PropertyDescriptor>& properties
+ ///< Vector of descriptors for the properties to be defined
+ );
+
+ /// Checks if an object is an instance created by a constructor function.
+ ///
+ /// This is equivalent to the JavaScript `instanceof` operator.
+ bool InstanceOf(
+ const Function& constructor ///< Constructor function
+ ) const;
+ };
+
+ template <typename T>
+ class External : public Value {
+ public:
+ static External New(napi_env env, T* data);
+
+ // Finalizer must implement `void operator()(Env env, T* data)`.
+ template <typename Finalizer>
+ static External New(napi_env env,
+ T* data,
+ Finalizer finalizeCallback);
+ // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`.
+ template <typename Finalizer, typename Hint>
+ static External New(napi_env env,
+ T* data,
+ Finalizer finalizeCallback,
+ Hint* finalizeHint);
+
+ External();
+ External(napi_env env, napi_value value);
+
+ T* Data() const;
+ };
+
+ class Array : public Object {
+ public:
+ static Array New(napi_env env);
+ static Array New(napi_env env, size_t length);
+
+ Array();
+ Array(napi_env env, napi_value value);
+
+ uint32_t Length() const;
+ };
+
+ /// A JavaScript array buffer value.
+ class ArrayBuffer : public Object {
+ public:
+ /// Creates a new ArrayBuffer instance over a new automatically-allocated buffer.
+ static ArrayBuffer New(
+ napi_env env, ///< N-API environment
+ size_t byteLength ///< Length of the buffer to be allocated, in bytes
+ );
+
+ /// Creates a new ArrayBuffer instance, using an external buffer with specified byte length.
+ static ArrayBuffer New(
+ napi_env env, ///< N-API environment
+ void* externalData, ///< Pointer to the external buffer to be used by the array
+ size_t byteLength ///< Length of the external buffer to be used by the array, in bytes
+ );
+
+ /// Creates a new ArrayBuffer instance, using an external buffer with specified byte length.
+ template <typename Finalizer>
+ static ArrayBuffer New(
+ napi_env env, ///< N-API environment
+ void* externalData, ///< Pointer to the external buffer to be used by the array
+ size_t byteLength, ///< Length of the external buffer to be used by the array,
+ /// in bytes
+ Finalizer finalizeCallback ///< Function to be called when the array buffer is destroyed;
+ /// must implement `void operator()(Env env, void* externalData)`
+ );
+
+ /// Creates a new ArrayBuffer instance, using an external buffer with specified byte length.
+ template <typename Finalizer, typename Hint>
+ static ArrayBuffer New(
+ napi_env env, ///< N-API environment
+ void* externalData, ///< Pointer to the external buffer to be used by the array
+ size_t byteLength, ///< Length of the external buffer to be used by the array,
+ /// in bytes
+ Finalizer finalizeCallback, ///< Function to be called when the array buffer is destroyed;
+ /// must implement `void operator()(Env env, void* externalData, Hint* hint)`
+ Hint* finalizeHint ///< Hint (second parameter) to be passed to the finalize callback
+ );
+
+ ArrayBuffer(); ///< Creates a new _empty_ ArrayBuffer instance.
+ ArrayBuffer(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ void* Data(); ///< Gets a pointer to the data buffer.
+ size_t ByteLength(); ///< Gets the length of the array buffer in bytes.
+
+ private:
+ mutable void* _data;
+ mutable size_t _length;
+
+ ArrayBuffer(napi_env env, napi_value value, void* data, size_t length);
+ void EnsureInfo() const;
+ };
+
+ /// A JavaScript typed-array value with unknown array type.
+ ///
+ /// For type-specific operations, cast to a `TypedArrayOf<T>` instance using the `As()`
+ /// method:
+ ///
+ /// Napi::TypedArray array = ...
+ /// if (t.TypedArrayType() == napi_int32_array) {
+ /// Napi::Int32Array int32Array = t.As<Napi::Int32Array>();
+ /// }
+ class TypedArray : public Object {
+ public:
+ TypedArray(); ///< Creates a new _empty_ TypedArray instance.
+ TypedArray(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ napi_typedarray_type TypedArrayType() const; ///< Gets the type of this typed-array.
+ Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer.
+
+ uint8_t ElementSize() const; ///< Gets the size in bytes of one element in the array.
+ size_t ElementLength() const; ///< Gets the number of elements in the array.
+ size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts.
+ size_t ByteLength() const; ///< Gets the length of the array in bytes.
+
+ protected:
+ /// !cond INTERNAL
+ napi_typedarray_type _type;
+ size_t _length;
+
+ TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length);
+
+ static const napi_typedarray_type unknown_array_type = static_cast<napi_typedarray_type>(-1);
+
+ template <typename T>
+ static
+#if defined(NAPI_HAS_CONSTEXPR)
+ constexpr
+#endif
+ napi_typedarray_type TypedArrayTypeForPrimitiveType() {
+ return std::is_same<T, int8_t>::value ? napi_int8_array
+ : std::is_same<T, uint8_t>::value ? napi_uint8_array
+ : std::is_same<T, int16_t>::value ? napi_int16_array
+ : std::is_same<T, uint16_t>::value ? napi_uint16_array
+ : std::is_same<T, int32_t>::value ? napi_int32_array
+ : std::is_same<T, uint32_t>::value ? napi_uint32_array
+ : std::is_same<T, float>::value ? napi_float32_array
+ : std::is_same<T, double>::value ? napi_float64_array
+// currently experimental guard with version of NAPI_VERSION that it is
+// released in once it is no longer experimental
+#if (NAPI_VERSION > 2147483646)
+ : std::is_same<T, int64_t>::value ? napi_bigint64_array
+ : std::is_same<T, uint64_t>::value ? napi_biguint64_array
+#endif // NAPI_EXPERIMENTAL
+ : unknown_array_type;
+ }
+ /// !endcond
+ };
+
+ /// A JavaScript typed-array value with known array type.
+ ///
+ /// Note while it is possible to create and access Uint8 "clamped" arrays using this class,
+ /// the _clamping_ behavior is only applied in JavaScript.
+ template <typename T>
+ class TypedArrayOf : public TypedArray {
+ public:
+ /// Creates a new TypedArray instance over a new automatically-allocated array buffer.
+ ///
+ /// The array type parameter can normally be omitted (because it is inferred from the template
+ /// parameter T), except when creating a "clamped" array:
+ ///
+ /// Uint8Array::New(env, length, napi_uint8_clamped_array)
+ static TypedArrayOf New(
+ napi_env env, ///< N-API environment
+ size_t elementLength, ///< Length of the created array, as a number of elements
+#if defined(NAPI_HAS_CONSTEXPR)
+ napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType<T>()
+#else
+ napi_typedarray_type type
+#endif
+ ///< Type of array, if different from the default array type for the template parameter T.
+ );
+
+ /// Creates a new TypedArray instance over a provided array buffer.
+ ///
+ /// The array type parameter can normally be omitted (because it is inferred from the template
+ /// parameter T), except when creating a "clamped" array:
+ ///
+ /// Uint8Array::New(env, length, buffer, 0, napi_uint8_clamped_array)
+ static TypedArrayOf New(
+ napi_env env, ///< N-API environment
+ size_t elementLength, ///< Length of the created array, as a number of elements
+ Napi::ArrayBuffer arrayBuffer, ///< Backing array buffer instance to use
+ size_t bufferOffset, ///< Offset into the array buffer where the typed-array starts
+#if defined(NAPI_HAS_CONSTEXPR)
+ napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType<T>()
+#else
+ napi_typedarray_type type
+#endif
+ ///< Type of array, if different from the default array type for the template parameter T.
+ );
+
+ TypedArrayOf(); ///< Creates a new _empty_ TypedArrayOf instance.
+ TypedArrayOf(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ T& operator [](size_t index); ///< Gets or sets an element in the array.
+ const T& operator [](size_t index) const; ///< Gets an element in the array.
+
+ /// Gets a pointer to the array's backing buffer.
+ ///
+ /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the
+ /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`.
+ T* Data();
+
+ /// Gets a pointer to the array's backing buffer.
+ ///
+ /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, because the
+ /// typed-array may have a non-zero `ByteOffset()` into the `ArrayBuffer`.
+ const T* Data() const;
+
+ private:
+ T* _data;
+
+ TypedArrayOf(napi_env env,
+ napi_value value,
+ napi_typedarray_type type,
+ size_t length,
+ T* data);
+ };
+
+ /// The DataView provides a low-level interface for reading/writing multiple
+ /// number types in an ArrayBuffer irrespective of the platform's endianness.
+ class DataView : public Object {
+ public:
+ static DataView New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer);
+ static DataView New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer,
+ size_t byteOffset);
+ static DataView New(napi_env env,
+ Napi::ArrayBuffer arrayBuffer,
+ size_t byteOffset,
+ size_t byteLength);
+
+ DataView(); ///< Creates a new _empty_ DataView instance.
+ DataView(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
+
+ Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer.
+ size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts.
+ size_t ByteLength() const; ///< Gets the length of the array in bytes.
+
+ void* Data() const;
+
+ float GetFloat32(size_t byteOffset) const;
+ double GetFloat64(size_t byteOffset) const;
+ int8_t GetInt8(size_t byteOffset) const;
+ int16_t GetInt16(size_t byteOffset) const;
+ int32_t GetInt32(size_t byteOffset) const;
+ uint8_t GetUint8(size_t byteOffset) const;
+ uint16_t GetUint16(size_t byteOffset) const;
+ uint32_t GetUint32(size_t byteOffset) const;
+
+ void SetFloat32(size_t byteOffset, float value) const;
+ void SetFloat64(size_t byteOffset, double value) const;
+ void SetInt8(size_t byteOffset, int8_t value) const;
+ void SetInt16(size_t byteOffset, int16_t value) const;
+ void SetInt32(size_t byteOffset, int32_t value) const;
+ void SetUint8(size_t byteOffset, uint8_t value) const;
+ void SetUint16(size_t byteOffset, uint16_t value) const;
+ void SetUint32(size_t byteOffset, uint32_t value) const;
+
+ private:
+ template <typename T>
+ T ReadData(size_t byteOffset) const;
+
+ template <typename T>
+ void WriteData(size_t byteOffset, T value) const;
+
+ void* _data;
+ size_t _length;
+ };
+
+ class Function : public Object {
+ public:
+ /// Callable must implement operator() accepting a const CallbackInfo&
+ /// and return either void or Value.
+ template <typename Callable>
+ static Function New(napi_env env,
+ Callable cb,
+ const char* utf8name = nullptr,
+ void* data = nullptr);
+ /// Callable must implement operator() accepting a const CallbackInfo&
+ /// and return either void or Value.
+ template <typename Callable>
+ static Function New(napi_env env,
+ Callable cb,
+ const std::string& utf8name,
+ void* data = nullptr);
+
+ Function();
+ Function(napi_env env, napi_value value);
+
+ Value operator ()(const std::initializer_list<napi_value>& args) const;
+
+ Value Call(const std::initializer_list<napi_value>& args) const;
+ Value Call(const std::vector<napi_value>& args) const;
+ Value Call(size_t argc, const napi_value* args) const;
+ Value Call(napi_value recv, const std::initializer_list<napi_value>& args) const;
+ Value Call(napi_value recv, const std::vector<napi_value>& args) const;
+ Value Call(napi_value recv, size_t argc, const napi_value* args) const;
+
+ Value MakeCallback(napi_value recv,
+ const std::initializer_list<napi_value>& args,
+ napi_async_context context = nullptr) const;
+ Value MakeCallback(napi_value recv,
+ const std::vector<napi_value>& args,
+ napi_async_context context = nullptr) const;
+ Value MakeCallback(napi_value recv,
+ size_t argc,
+ const napi_value* args,
+ napi_async_context context = nullptr) const;
+
+ Object New(const std::initializer_list<napi_value>& args) const;
+ Object New(const std::vector<napi_value>& args) const;
+ Object New(size_t argc, const napi_value* args) const;
+ };
+
+ class Promise : public Object {
+ public:
+ class Deferred {
+ public:
+ static Deferred New(napi_env env);
+ Deferred(napi_env env);
+
+ Napi::Promise Promise() const;
+ Napi::Env Env() const;
+
+ void Resolve(napi_value value) const;
+ void Reject(napi_value value) const;
+
+ private:
+ napi_env _env;
+ napi_deferred _deferred;
+ napi_value _promise;
+ };
+
+ Promise(napi_env env, napi_value value);
+ };
+
+ template <typename T>
+ class Buffer : public Uint8Array {
+ public:
+ static Buffer<T> New(napi_env env, size_t length);
+ static Buffer<T> New(napi_env env, T* data, size_t length);
+
+ // Finalizer must implement `void operator()(Env env, T* data)`.
+ template <typename Finalizer>
+ static Buffer<T> New(napi_env env, T* data,
+ size_t length,
+ Finalizer finalizeCallback);
+ // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`.
+ template <typename Finalizer, typename Hint>
+ static Buffer<T> New(napi_env env, T* data,
+ size_t length,
+ Finalizer finalizeCallback,
+ Hint* finalizeHint);
+
+ static Buffer<T> Copy(napi_env env, const T* data, size_t length);
+
+ Buffer();
+ Buffer(napi_env env, napi_value value);
+ size_t Length() const;
+ T* Data() const;
+
+ private:
+ mutable size_t _length;
+ mutable T* _data;
+
+ Buffer(napi_env env, napi_value value, size_t length, T* data);
+ void EnsureInfo() const;
+ };
+
+ /// Holds a counted reference to a value; initially a weak reference unless otherwise specified,
+ /// may be changed to/from a strong reference by adjusting the refcount.
+ ///
+ /// The referenced value is not immediately destroyed when the reference count is zero; it is
+ /// merely then eligible for garbage-collection if there are no other references to the value.
+ template <typename T>
+ class Reference {
+ public:
+ static Reference<T> New(const T& value, uint32_t initialRefcount = 0);
+
+ Reference();
+ Reference(napi_env env, napi_ref ref);
+ ~Reference();
+
+ // A reference can be moved but cannot be copied.
+ Reference(Reference<T>&& other);
+ Reference<T>& operator =(Reference<T>&& other);
+ Reference<T>& operator =(Reference<T>&) = delete;
+
+ operator napi_ref() const;
+ bool operator ==(const Reference<T> &other) const;
+ bool operator !=(const Reference<T> &other) const;
+
+ Napi::Env Env() const;
+ bool IsEmpty() const;
+
+ // Note when getting the value of a Reference it is usually correct to do so
+ // within a HandleScope so that the value handle gets cleaned up efficiently.
+ T Value() const;
+
+ uint32_t Ref();
+ uint32_t Unref();
+ void Reset();
+ void Reset(const T& value, uint32_t refcount = 0);
+
+ // Call this on a reference that is declared as static data, to prevent its destructor
+ // from running at program shutdown time, which would attempt to reset the reference when
+ // the environment is no longer valid.
+ void SuppressDestruct();
+
+ protected:
+ Reference(const Reference<T>&);
+
+ /// !cond INTERNAL
+ napi_env _env;
+ napi_ref _ref;
+ /// !endcond
+
+ private:
+ bool _suppressDestruct;
+ };
+
+ class ObjectReference: public Reference<Object> {
+ public:
+ ObjectReference();
+ ObjectReference(napi_env env, napi_ref ref);
+
+ // A reference can be moved but cannot be copied.
+ ObjectReference(Reference<Object>&& other);
+ ObjectReference& operator =(Reference<Object>&& other);
+ ObjectReference(ObjectReference&& other);
+ ObjectReference& operator =(ObjectReference&& other);
+ ObjectReference& operator =(ObjectReference&) = delete;
+
+ Napi::Value Get(const char* utf8name) const;
+ Napi::Value Get(const std::string& utf8name) const;
+ void Set(const char* utf8name, napi_value value);
+ void Set(const char* utf8name, Napi::Value value);
+ void Set(const char* utf8name, const char* utf8value);
+ void Set(const char* utf8name, bool boolValue);
+ void Set(const char* utf8name, double numberValue);
+ void Set(const std::string& utf8name, napi_value value);
+ void Set(const std::string& utf8name, Napi::Value value);
+ void Set(const std::string& utf8name, std::string& utf8value);
+ void Set(const std::string& utf8name, bool boolValue);
+ void Set(const std::string& utf8name, double numberValue);
+
+ Napi::Value Get(uint32_t index) const;
+ void Set(uint32_t index, const napi_value value);
+ void Set(uint32_t index, const Napi::Value value);
+ void Set(uint32_t index, const char* utf8value);
+ void Set(uint32_t index, const std::string& utf8value);
+ void Set(uint32_t index, bool boolValue);
+ void Set(uint32_t index, double numberValue);
+
+ protected:
+ ObjectReference(const ObjectReference&);
+ };
+
+ class FunctionReference: public Reference<Function> {
+ public:
+ FunctionReference();
+ FunctionReference(napi_env env, napi_ref ref);
+
+ // A reference can be moved but cannot be copied.
+ FunctionReference(Reference<Function>&& other);
+ FunctionReference& operator =(Reference<Function>&& other);
+ FunctionReference(FunctionReference&& other);
+ FunctionReference& operator =(FunctionReference&& other);
+ FunctionReference(const FunctionReference&) = delete;
+ FunctionReference& operator =(FunctionReference&) = delete;
+
+ Napi::Value operator ()(const std::initializer_list<napi_value>& args) const;
+
+ Napi::Value Call(const std::initializer_list<napi_value>& args) const;
+ Napi::Value Call(const std::vector<napi_value>& args) const;
+ Napi::Value Call(napi_value recv, const std::initializer_list<napi_value>& args) const;
+ Napi::Value Call(napi_value recv, const std::vector<napi_value>& args) const;
+ Napi::Value Call(napi_value recv, size_t argc, const napi_value* args) const;
+
+ Napi::Value MakeCallback(napi_value recv,
+ const std::initializer_list<napi_value>& args,
+ napi_async_context context = nullptr) const;
+ Napi::Value MakeCallback(napi_value recv,
+ const std::vector<napi_value>& args,
+ napi_async_context context = nullptr) const;
+ Napi::Value MakeCallback(napi_value recv,
+ size_t argc,
+ const napi_value* args,
+ napi_async_context context = nullptr) const;
+
+ Object New(const std::initializer_list<napi_value>& args) const;
+ Object New(const std::vector<napi_value>& args) const;
+ };
+
+ // Shortcuts to creating a new reference with inferred type and refcount = 0.
+ template <typename T> Reference<T> Weak(T value);
+ ObjectReference Weak(Object value);
+ FunctionReference Weak(Function value);
+
+ // Shortcuts to creating a new reference with inferred type and refcount = 1.
+ template <typename T> Reference<T> Persistent(T value);
+ ObjectReference Persistent(Object value);
+ FunctionReference Persistent(Function value);
+
+ /// A persistent reference to a JavaScript error object. Use of this class depends somewhat
+ /// on whether C++ exceptions are enabled at compile time.
+ ///
+ /// ### Handling Errors With C++ Exceptions
+ ///
+ /// If C++ exceptions are enabled, then the `Error` class extends `std::exception` and enables
+ /// integrated error-handling for C++ exceptions and JavaScript exceptions.
+ ///
+ /// If a N-API call fails without executing any JavaScript code (for example due to an invalid
+ /// argument), then the N-API wrapper automatically converts and throws the error as a C++
+ /// exception of type `Napi::Error`. Or if a JavaScript function called by C++ code via N-API
+ /// throws a JavaScript exception, then the N-API wrapper automatically converts and throws it as
+ /// a C++ exception of type `Napi::Error`.
+ ///
+ /// If a C++ exception of type `Napi::Error` escapes from a N-API C++ callback, then the N-API
+ /// wrapper automatically converts and throws it as a JavaScript exception. Therefore, catching
+ /// a C++ exception of type `Napi::Error` prevents a JavaScript exception from being thrown.
+ ///
+ /// #### Example 1A - Throwing a C++ exception:
+ ///
+ /// Napi::Env env = ...
+ /// throw Napi::Error::New(env, "Example exception");
+ ///
+ /// Following C++ statements will not be executed. The exception will bubble up as a C++
+ /// exception of type `Napi::Error`, until it is either caught while still in C++, or else
+ /// automatically propataged as a JavaScript exception when the callback returns to JavaScript.
+ ///
+ /// #### Example 2A - Propagating a N-API C++ exception:
+ ///
+ /// Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
+ /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
+ ///
+ /// Following C++ statements will not be executed. The exception will bubble up as a C++
+ /// exception of type `Napi::Error`, until it is either caught while still in C++, or else
+ /// automatically propagated as a JavaScript exception when the callback returns to JavaScript.
+ ///
+ /// #### Example 3A - Handling a N-API C++ exception:
+ ///
+ /// Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
+ /// Napi::Value result;
+ /// try {
+ /// result = jsFunctionThatThrows({ arg1, arg2 });
+ /// } catch (const Napi::Error& e) {
+ /// cerr << "Caught JavaScript exception: " + e.what();
+ /// }
+ ///
+ /// Since the exception was caught here, it will not be propagated as a JavaScript exception.
+ ///
+ /// ### Handling Errors Without C++ Exceptions
+ ///
+ /// If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`) then this class
+ /// does not extend `std::exception`, and APIs in the `Napi` namespace do not throw C++
+ /// exceptions when they fail. Instead, they raise _pending_ JavaScript exceptions and
+ /// return _empty_ `Value`s. Calling code should check `Value::IsEmpty()` before attempting
+ /// to use a returned value, and may use methods on the `Env` class to check for, get, and
+ /// clear a pending JavaScript exception. If the pending exception is not cleared, it will
+ /// be thrown when the native callback returns to JavaScript.
+ ///
+ /// #### Example 1B - Throwing a JS exception
+ ///
+ /// Napi::Env env = ...
+ /// Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException();
+ /// return;
+ ///
+ /// After throwing a JS exception, the code should generally return immediately from the native
+ /// callback, after performing any necessary cleanup.
+ ///
+ /// #### Example 2B - Propagating a N-API JS exception:
+ ///
+ /// Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
+ /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
+ /// if (result.IsEmpty()) return;
+ ///
+ /// An empty value result from a N-API call indicates an error occurred, and a JavaScript
+ /// exception is pending. To let the exception propagate, the code should generally return
+ /// immediately from the native callback, after performing any necessary cleanup.
+ ///
+ /// #### Example 3B - Handling a N-API JS exception:
+ ///
+ /// Napi::Function jsFunctionThatThrows = someObj.As<Napi::Function>();
+ /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 });
+ /// if (result.IsEmpty()) {
+ /// Napi::Error e = env.GetAndClearPendingException();
+ /// cerr << "Caught JavaScript exception: " + e.Message();
+ /// }
+ ///
+ /// Since the exception was cleared here, it will not be propagated as a JavaScript exception
+ /// after the native callback returns.
+ class Error : public ObjectReference
+#ifdef NAPI_CPP_EXCEPTIONS
+ , public std::exception
+#endif // NAPI_CPP_EXCEPTIONS
+ {
+ public:
+ static Error New(napi_env env);
+ static Error New(napi_env env, const char* message);
+ static Error New(napi_env env, const std::string& message);
+
+ static NAPI_NO_RETURN void Fatal(const char* location, const char* message);
+
+ Error();
+ Error(napi_env env, napi_value value);
+
+ // An error can be moved or copied.
+ Error(Error&& other);
+ Error& operator =(Error&& other);
+ Error(const Error&);
+ Error& operator =(Error&);
+
+ const std::string& Message() const NAPI_NOEXCEPT;
+ void ThrowAsJavaScriptException() const;
+
+#ifdef NAPI_CPP_EXCEPTIONS
+ const char* what() const NAPI_NOEXCEPT override;
+#endif // NAPI_CPP_EXCEPTIONS
+
+ protected:
+ /// !cond INTERNAL
+ typedef napi_status (*create_error_fn)(napi_env envb, napi_value code, napi_value msg, napi_value* result);
+
+ template <typename TError>
+ static TError New(napi_env env,
+ const char* message,
+ size_t length,
+ create_error_fn create_error);
+ /// !endcond
+
+ private:
+ mutable std::string _message;
+ };
+
+ class TypeError : public Error {
+ public:
+ static TypeError New(napi_env env, const char* message);
+ static TypeError New(napi_env env, const std::string& message);
+
+ TypeError();
+ TypeError(napi_env env, napi_value value);
+ };
+
+ class RangeError : public Error {
+ public:
+ static RangeError New(napi_env env, const char* message);
+ static RangeError New(napi_env env, const std::string& message);
+
+ RangeError();
+ RangeError(napi_env env, napi_value value);
+ };
+
+ class CallbackInfo {
+ public:
+ CallbackInfo(napi_env env, napi_callback_info info);
+ ~CallbackInfo();
+
+ // Disallow copying to prevent multiple free of _dynamicArgs
+ CallbackInfo(CallbackInfo const &) = delete;
+ void operator=(CallbackInfo const &) = delete;
+
+ Napi::Env Env() const;
+ Value NewTarget() const;
+ bool IsConstructCall() const;
+ size_t Length() const;
+ const Value operator [](size_t index) const;
+ Value This() const;
+ void* Data() const;
+ void SetData(void* data);
+
+ private:
+ const size_t _staticArgCount = 6;
+ napi_env _env;
+ napi_callback_info _info;
+ napi_value _this;
+ size_t _argc;
+ napi_value* _argv;
+ napi_value _staticArgs[6];
+ napi_value* _dynamicArgs;
+ void* _data;
+ };
+
+ class PropertyDescriptor {
+ public:
+#ifndef NODE_ADDON_API_DISABLE_DEPRECATED
+ template <typename Getter>
+ static PropertyDescriptor Accessor(const char* utf8name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter>
+ static PropertyDescriptor Accessor(const std::string& utf8name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter>
+ static PropertyDescriptor Accessor(napi_value name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter>
+ static PropertyDescriptor Accessor(Name name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(const char* utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(const std::string& utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(napi_value name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(Name name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(const char* utf8name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(const std::string& utf8name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(napi_value name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(Name name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+#endif // !NODE_ADDON_API_DISABLE_DEPRECATED
+
+ template <typename Getter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ const char* utf8name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ Name name,
+ Getter getter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ const char* utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Getter, typename Setter>
+ static PropertyDescriptor Accessor(Napi::Env env,
+ Napi::Object object,
+ Name name,
+ Getter getter,
+ Setter setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(Napi::Env env,
+ Napi::Object object,
+ const char* utf8name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(Napi::Env env,
+ Napi::Object object,
+ const std::string& utf8name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ template <typename Callable>
+ static PropertyDescriptor Function(Napi::Env env,
+ Napi::Object object,
+ Name name,
+ Callable cb,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor Value(const char* utf8name,
+ napi_value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor Value(const std::string& utf8name,
+ napi_value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor Value(napi_value name,
+ napi_value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor Value(Name name,
+ Napi::Value value,
+ napi_property_attributes attributes = napi_default);
+
+ PropertyDescriptor(napi_property_descriptor desc);
+
+ operator napi_property_descriptor&();
+ operator const napi_property_descriptor&() const;
+
+ private:
+ napi_property_descriptor _desc;
+ };
+
+ /// Property descriptor for use with `ObjectWrap::DefineClass()`.
+ ///
+ /// This is different from the standalone `PropertyDescriptor` because it is specific to each
+ /// `ObjectWrap<T>` subclass. This prevents using descriptors from a different class when
+ /// defining a new class (preventing the callbacks from having incorrect `this` pointers).
+ template <typename T>
+ class ClassPropertyDescriptor {
+ public:
+ ClassPropertyDescriptor(napi_property_descriptor desc) : _desc(desc) {}
+
+ operator napi_property_descriptor&() { return _desc; }
+ operator const napi_property_descriptor&() const { return _desc; }
+
+ private:
+ napi_property_descriptor _desc;
+ };
+
+ /// Base class to be extended by C++ classes exposed to JavaScript; each C++ class instance gets
+ /// "wrapped" by a JavaScript object that is managed by this class.
+ ///
+ /// At initialization time, the `DefineClass()` method must be used to
+ /// hook up the accessor and method callbacks. It takes a list of
+ /// property descriptors, which can be constructed via the various
+ /// static methods on the base class.
+ ///
+ /// #### Example:
+ ///
+ /// class Example: public Napi::ObjectWrap<Example> {
+ /// public:
+ /// static void Initialize(Napi::Env& env, Napi::Object& target) {
+ /// Napi::Function constructor = DefineClass(env, "Example", {
+ /// InstanceAccessor("value", &Example::GetSomething, &Example::SetSomething),
+ /// InstanceMethod("doSomething", &Example::DoSomething),
+ /// });
+ /// target.Set("Example", constructor);
+ /// }
+ ///
+ /// Example(const Napi::CallbackInfo& info); // Constructor
+ /// Napi::Value GetSomething(const Napi::CallbackInfo& info);
+ /// void SetSomething(const Napi::CallbackInfo& info, const Napi::Value& value);
+ /// Napi::Value DoSomething(const Napi::CallbackInfo& info);
+ /// }
+ template <typename T>
+ class ObjectWrap : public Reference<Object> {
+ public:
+ ObjectWrap(const CallbackInfo& callbackInfo);
+
+ static T* Unwrap(Object wrapper);
+
+ // Methods exposed to JavaScript must conform to one of these callback signatures.
+ typedef void (*StaticVoidMethodCallback)(const CallbackInfo& info);
+ typedef Napi::Value (*StaticMethodCallback)(const CallbackInfo& info);
+ typedef Napi::Value (*StaticGetterCallback)(const CallbackInfo& info);
+ typedef void (*StaticSetterCallback)(const CallbackInfo& info, const Napi::Value& value);
+ typedef void (T::*InstanceVoidMethodCallback)(const CallbackInfo& info);
+ typedef Napi::Value (T::*InstanceMethodCallback)(const CallbackInfo& info);
+ typedef Napi::Value (T::*InstanceGetterCallback)(const CallbackInfo& info);
+ typedef void (T::*InstanceSetterCallback)(const CallbackInfo& info, const Napi::Value& value);
+
+ typedef ClassPropertyDescriptor<T> PropertyDescriptor;
+
+ static Function DefineClass(Napi::Env env,
+ const char* utf8name,
+ const std::initializer_list<PropertyDescriptor>& properties,
+ void* data = nullptr);
+ static Function DefineClass(Napi::Env env,
+ const char* utf8name,
+ const std::vector<PropertyDescriptor>& properties,
+ void* data = nullptr);
+ static PropertyDescriptor StaticMethod(const char* utf8name,
+ StaticVoidMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticMethod(const char* utf8name,
+ StaticMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticMethod(Symbol name,
+ StaticVoidMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticMethod(Symbol name,
+ StaticMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticAccessor(const char* utf8name,
+ StaticGetterCallback getter,
+ StaticSetterCallback setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticAccessor(Symbol name,
+ StaticGetterCallback getter,
+ StaticSetterCallback setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceMethod(const char* utf8name,
+ InstanceVoidMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceMethod(const char* utf8name,
+ InstanceMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceMethod(Symbol name,
+ InstanceVoidMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceMethod(Symbol name,
+ InstanceMethodCallback method,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceAccessor(const char* utf8name,
+ InstanceGetterCallback getter,
+ InstanceSetterCallback setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor InstanceAccessor(Symbol name,
+ InstanceGetterCallback getter,
+ InstanceSetterCallback setter,
+ napi_property_attributes attributes = napi_default,
+ void* data = nullptr);
+ static PropertyDescriptor StaticValue(const char* utf8name,
+ Napi::Value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor StaticValue(Symbol name,
+ Napi::Value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor InstanceValue(const char* utf8name,
+ Napi::Value value,
+ napi_property_attributes attributes = napi_default);
+ static PropertyDescriptor InstanceValue(Symbol name,
+ Napi::Value value,
+ napi_property_attributes attributes = napi_default);
+
+ private:
+ static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value StaticMethodCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value StaticGetterCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value StaticSetterCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value InstanceVoidMethodCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value InstanceMethodCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value InstanceGetterCallbackWrapper(napi_env env, napi_callback_info info);
+ static napi_value InstanceSetterCallbackWrapper(napi_env env, napi_callback_info info);
+ static void FinalizeCallback(napi_env env, void* data, void* hint);
+ static Function DefineClass(Napi::Env env,
+ const char* utf8name,
+ const size_t props_count,
+ const napi_property_descriptor* props,
+ void* data = nullptr);
+
+ template <typename TCallback>
+ struct MethodCallbackData {
+ TCallback callback;
+ void* data;
+ };
+ typedef MethodCallbackData<StaticVoidMethodCallback> StaticVoidMethodCallbackData;
+ typedef MethodCallbackData<StaticMethodCallback> StaticMethodCallbackData;
+ typedef MethodCallbackData<InstanceVoidMethodCallback> InstanceVoidMethodCallbackData;
+ typedef MethodCallbackData<InstanceMethodCallback> InstanceMethodCallbackData;
+
+ template <typename TGetterCallback, typename TSetterCallback>
+ struct AccessorCallbackData {
+ TGetterCallback getterCallback;
+ TSetterCallback setterCallback;
+ void* data;
+ };
+ typedef AccessorCallbackData<StaticGetterCallback, StaticSetterCallback>
+ StaticAccessorCallbackData;
+ typedef AccessorCallbackData<InstanceGetterCallback, InstanceSetterCallback>
+ InstanceAccessorCallbackData;
+ };
+
+ class HandleScope {
+ public:
+ HandleScope(napi_env env, napi_handle_scope scope);
+ explicit HandleScope(Napi::Env env);
+ ~HandleScope();
+
+ operator napi_handle_scope() const;
+
+ Napi::Env Env() const;
+
+ private:
+ napi_env _env;
+ napi_handle_scope _scope;
+ };
+
+ class EscapableHandleScope {
+ public:
+ EscapableHandleScope(napi_env env, napi_escapable_handle_scope scope);
+ explicit EscapableHandleScope(Napi::Env env);
+ ~EscapableHandleScope();
+
+ operator napi_escapable_handle_scope() const;
+
+ Napi::Env Env() const;
+ Value Escape(napi_value escapee);
+
+ private:
+ napi_env _env;
+ napi_escapable_handle_scope _scope;
+ };
+
+#if (NAPI_VERSION > 2)
+ class CallbackScope {
+ public:
+ CallbackScope(napi_env env, napi_callback_scope scope);
+ CallbackScope(napi_env env, napi_async_context context);
+ virtual ~CallbackScope();
+
+ operator napi_callback_scope() const;
+
+ Napi::Env Env() const;
+
+ private:
+ napi_env _env;
+ napi_callback_scope _scope;
+ };
+#endif
+
+ class AsyncContext {
+ public:
+ explicit AsyncContext(napi_env env, const char* resource_name);
+ explicit AsyncContext(napi_env env, const char* resource_name, const Object& resource);
+ virtual ~AsyncContext();
+
+ AsyncContext(AsyncContext&& other);
+ AsyncContext& operator =(AsyncContext&& other);
+ AsyncContext(const AsyncContext&) = delete;
+ AsyncContext& operator =(AsyncContext&) = delete;
+
+ operator napi_async_context() const;
+
+ private:
+ napi_env _env;
+ napi_async_context _context;
+ };
+
+ class AsyncWorker {
+ public:
+ virtual ~AsyncWorker();
+
+ // An async worker can be moved but cannot be copied.
+ AsyncWorker(AsyncWorker&& other);
+ AsyncWorker& operator =(AsyncWorker&& other);
+ AsyncWorker(const AsyncWorker&) = delete;
+ AsyncWorker& operator =(AsyncWorker&) = delete;
+
+ operator napi_async_work() const;
+
+ Napi::Env Env() const;
+
+ void Queue();
+ void Cancel();
+ void SuppressDestruct();
+
+ ObjectReference& Receiver();
+ FunctionReference& Callback();
+
+ protected:
+ explicit AsyncWorker(const Function& callback);
+ explicit AsyncWorker(const Function& callback,
+ const char* resource_name);
+ explicit AsyncWorker(const Function& callback,
+ const char* resource_name,
+ const Object& resource);
+ explicit AsyncWorker(const Object& receiver,
+ const Function& callback);
+ explicit AsyncWorker(const Object& receiver,
+ const Function& callback,
+ const char* resource_name);
+ explicit AsyncWorker(const Object& receiver,
+ const Function& callback,
+ const char* resource_name,
+ const Object& resource);
+
+ explicit AsyncWorker(Napi::Env env);
+ explicit AsyncWorker(Napi::Env env,
+ const char* resource_name);
+ explicit AsyncWorker(Napi::Env env,
+ const char* resource_name,
+ const Object& resource);
+
+ virtual void Execute() = 0;
+ virtual void OnOK();
+ virtual void OnError(const Error& e);
+ virtual void Destroy();
+ virtual std::vector<napi_value> GetResult(Napi::Env env);
+
+ void SetError(const std::string& error);
+
+ private:
+ static void OnExecute(napi_env env, void* this_pointer);
+ static void OnWorkComplete(napi_env env,
+ napi_status status,
+ void* this_pointer);
+
+ napi_env _env;
+ napi_async_work _work;
+ ObjectReference _receiver;
+ FunctionReference _callback;
+ std::string _error;
+ bool _suppress_destruct;
+ };
+
+ #if (NAPI_VERSION > 3)
+ class ThreadSafeFunction {
+ public:
+ // This API may only be called from the main thread.
+ template <typename ResourceString>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename Finalizer>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename Finalizer,
+ typename FinalizerDataType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType, typename Finalizer>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename Finalizer>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename Finalizer,
+ typename FinalizerDataType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType, typename Finalizer>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback);
+
+ // This API may only be called from the main thread.
+ template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data);
+
+ ThreadSafeFunction();
+ ThreadSafeFunction(napi_threadsafe_function tsFunctionValue);
+
+ ThreadSafeFunction(ThreadSafeFunction&& other);
+ ThreadSafeFunction& operator=(ThreadSafeFunction&& other);
+
+ // This API may be called from any thread.
+ napi_status BlockingCall() const;
+
+ // This API may be called from any thread.
+ template <typename Callback>
+ napi_status BlockingCall(Callback callback) const;
+
+ // This API may be called from any thread.
+ template <typename DataType, typename Callback>
+ napi_status BlockingCall(DataType* data, Callback callback) const;
+
+ // This API may be called from any thread.
+ napi_status NonBlockingCall() const;
+
+ // This API may be called from any thread.
+ template <typename Callback>
+ napi_status NonBlockingCall(Callback callback) const;
+
+ // This API may be called from any thread.
+ template <typename DataType, typename Callback>
+ napi_status NonBlockingCall(DataType* data, Callback callback) const;
+
+ // This API may be called from any thread.
+ napi_status Acquire() const;
+
+ // This API may be called from any thread.
+ napi_status Release();
+
+ // This API may be called from any thread.
+ napi_status Abort();
+
+ struct ConvertibleContext
+ {
+ template <class T>
+ operator T*() { return static_cast<T*>(context); }
+ void* context;
+ };
+
+ // This API may be called from any thread.
+ ConvertibleContext GetContext() const;
+
+ private:
+ using CallbackWrapper = std::function<void(Napi::Env, Napi::Function)>;
+
+ template <typename ResourceString, typename ContextType,
+ typename Finalizer, typename FinalizerDataType>
+ static ThreadSafeFunction New(napi_env env,
+ const Function& callback,
+ const Object& resource,
+ ResourceString resourceName,
+ size_t maxQueueSize,
+ size_t initialThreadCount,
+ ContextType* context,
+ Finalizer finalizeCallback,
+ FinalizerDataType* data,
+ napi_finalize wrapper);
+
+ napi_status CallInternal(CallbackWrapper* callbackWrapper,
+ napi_threadsafe_function_call_mode mode) const;
+
+ static void CallJS(napi_env env,
+ napi_value jsCallback,
+ void* context,
+ void* data);
+
+ std::unique_ptr<napi_threadsafe_function> _tsfn;
+ };
+ #endif
+
+ // Memory management.
+ class MemoryManagement {
+ public:
+ static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes);
+ };
+
+ // Version management
+ class VersionManagement {
+ public:
+ static uint32_t GetNapiVersion(Env env);
+ static const napi_node_version* GetNodeVersion(Env env);
+ };
+
+} // namespace Napi
+
+// Inline implementations of all the above class methods are included here.
+#include "napi-inl.h"
+
+#endif // SRC_NAPI_H_
@@ -42,6 +42,11 @@ if(NOT QT_FEATURE_system_zlib)
endif()
qt_install_3rdparty_library_wrap_config_extra_file(BundledZLIB)
+if(OHOS)
+ add_subdirectory(3rdparty/node-addon-api)
+ add_subdirectory(openharmony)
+endif()
+
add_subdirectory(corelib)
# Needs to be after corelib, because some of them reference Core.
@@ -772,7 +772,7 @@ qt_internal_extend_target(Core CONDITION APPLE
text/qlocale_mac.mm
)
-qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE
+qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE AND NOT OHOS
SOURCES
text/qlocale_unix.cpp
)
@@ -856,7 +856,12 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND ANDROID AND NOT
time/qtimezoneprivate_android.cpp
)
-qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND UNIX AND NOT ANDROID AND NOT APPLE
+qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND OHOS AND NOT APPLE
+ SOURCES
+ time/qtimezoneprivate_openharmony.cpp
+)
+
+qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND UNIX AND NOT ANDROID AND NOT OHOS AND NOT APPLE
SOURCES
time/qtimezoneprivate_tz.cpp
)
@@ -993,6 +998,45 @@ qt_internal_extend_target(Core CONDITION ANDROID
# place to put them.
)
+qt_internal_extend_target(Core CONDITION OHOS
+ SOURCES
+ global/qoperatingsystemversion_openharmony.cpp
+ kernel/qohutility.h
+ kernel/qohutility.cpp
+ kernel/qohoswatchdog.h
+ kernel/qohoswatchdog.cpp
+ kernel/qjsobject.h
+ kernel/qjsobject_p.h
+ kernel/qjsobject.cpp
+ kernel/qjsmodule.h
+ kernel/qjsmodule.cpp
+ kernel/qjspromise_p.h
+ kernel/qjspromise.cpp
+ kernel/qopenharmony.h
+ kernel/qopenharmony.cpp
+ kernel/qopenharmony_p.h
+ kernel/qopenharmonydefines.h
+ kernel/qohhiappevent_p.h
+ kernel/qnapi.h
+ io/qsettings_openharmony.cpp
+ io/qstandardpaths_openharmony.cpp
+ io/qstorageinfo_unix.cpp
+ text/qlocale_harmony.cpp
+ kernel/qpermissions_openharmony.cpp
+ PUBLIC_LIBRARIES
+ deviceinfo_ndk.z
+ ace_napi.z
+ hilog_ndk.z
+ ohhicollie
+ deviceinfo_ndk.z
+ ohfileuri
+ uv
+ asset_ndk.z
+ bundle_ndk.z
+ ohenvironment
+)
+
+
qt_internal_extend_target(Core CONDITION WIN32
SOURCES
platform/windows/qfactorycacheregistration_p.h
@@ -1008,7 +1052,7 @@ qt_internal_extend_target(Core CONDITION HAIKU AND NOT ANDROID
be
)
-qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE AND NOT HAIKU AND NOT ANDROID
+qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE AND NOT HAIKU AND NOT ANDROID AND NOT OHOS
SOURCES
io/qstandardpaths_unix.cpp
io/qstorageinfo_unix.cpp
@@ -599,6 +599,21 @@ function(_qt_internal_create_executable target)
PROPERTY _qt_android_apply_arch_suffix_called_from_qt_impl TRUE)
qt6_android_apply_arch_suffix("${target}")
set_property(TARGET "${target}" PROPERTY _qt_is_android_executable TRUE)
+ # elseif(OHOS)
+ # list(REMOVE_ITEM ARGN "WIN32" "MACOSX_BUNDLE")
+ # add_library("${target}" MODULE ${ARGN})
+ # # On our qmake builds we do don't compile the executables with
+ # # visibility=hidden. Not having this flag set will cause the
+ # # executable to have main() hidden and can then no longer be loaded
+ # # through dlopen()
+ # set_property(TARGET "${target}" PROPERTY C_VISIBILITY_PRESET default)
+ # set_property(TARGET "${target}" PROPERTY CXX_VISIBILITY_PRESET default)
+ # set_property(TARGET "${target}" PROPERTY OBJC_VISIBILITY_PRESET default)
+ # set_property(TARGET "${target}" PROPERTY OBJCXX_VISIBILITY_PRESET default)
+ # set_property(TARGET "${target}"
+ # PROPERTY _qt_openharmony_apply_arch_suffix_called_from_qt_impl TRUE)
+ # qt6_openharmony_apply_arch_suffix("${target}")
+ # set_property(TARGET "${target}" PROPERTY _qt_is_openharmony_executable TRUE)
else()
add_executable("${target}" ${ARGN})
endif()
@@ -740,8 +755,9 @@ function(qt6_finalize_target target)
_qt_internal_expose_deferred_files_to_ide(${target})
get_target_property(target_type ${target} TYPE)
get_target_property(is_android_executable "${target}" _qt_is_android_executable)
+ get_target_property(is_openharmony_executable "${target}" _is_openharmony_executable)
- if(target_type STREQUAL "EXECUTABLE" OR is_android_executable)
+ if(target_type STREQUAL "EXECUTABLE" OR is_android_executable OR is_openharmony_executable)
_qt_internal_finalize_executable(${ARGV})
endif()
@@ -2374,6 +2390,11 @@ function(_qt_internal_add_library target)
PROPERTY _qt_android_apply_arch_suffix_called_from_qt_impl TRUE)
qt6_android_apply_arch_suffix("${target}")
endif()
+ # if(OHOS)
+ # set_property(TARGET "${target}"
+ # PROPERTY _qt_openharmony_apply_arch_suffix_called_from_qt_impl TRUE)
+ # qt6_openharmony_apply_arch_suffix("${target}")
+ # endif()
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
new file mode 100644
@@ -0,0 +1,18 @@
+function(qt6_openharmony_apply_arch_suffix target)
+ get_target_property(called_from_qt_impl
+ ${target} _qt_openharmony_apply_arch_suffix_called_from_qt_impl)
+ if(called_from_qt_impl)
+ # Don't show deprecation when called by our own function implementations.
+ else()
+ message(DEPRECATION
+ "Calling qt_openharmony_apply_arch_suffix directly is deprecated since Qt 6.5. "
+ "Use qt_add_executable or qt_add_library instead.")
+ endif()
+
+ get_target_property(target_type ${target} TYPE)
+ if (target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
+ set_property(TARGET "${target}" PROPERTY SUFFIX "_${CMAKE_OPENHARMONY_ARCH_ABI}.so")
+ elseif (target_type STREQUAL "STATIC_LIBRARY")
+ set_property(TARGET "${target}" PROPERTY SUFFIX "_${CMAKE_OPENHARMONY_ARCH_ABI}.a")
+ endif()
+endfunction()
@@ -705,7 +705,7 @@ qt_feature("sharedmemory" PUBLIC
SECTION "Kernel"
LABEL "QSharedMemory"
PURPOSE "Provides access to a shared memory segment."
- CONDITION ( ANDROID OR WIN32 OR ( NOT VXWORKS AND ( TEST_ipc_sysv OR TEST_ipc_posix ) ) )
+ CONDITION (OHOS OR ANDROID OR WIN32 OR ( NOT VXWORKS AND ( TEST_ipc_sysv OR TEST_ipc_posix ) ) )
)
qt_feature_definition("sharedmemory" "QT_NO_SHAREDMEMORY" NEGATE VALUE "1")
qt_feature("shortcut" PUBLIC
@@ -718,7 +718,7 @@ qt_feature("systemsemaphore" PUBLIC
SECTION "Kernel"
LABEL "QSystemSemaphore"
PURPOSE "Provides a general counting system semaphore."
- CONDITION ( NOT INTEGRITY AND NOT VXWORKS AND NOT rtems ) AND ( ANDROID OR WIN32 OR TEST_ipc_sysv OR TEST_ipc_posix )
+ CONDITION ( NOT INTEGRITY AND NOT VXWORKS AND NOT rtems ) AND (OHOS OR ANDROID OR WIN32 OR TEST_ipc_sysv OR TEST_ipc_posix )
)
qt_feature_definition("systemsemaphore" "QT_NO_SYSTEMSEMAPHORE" NEGATE VALUE "1")
qt_feature("xmlstream" PUBLIC
@@ -963,7 +963,7 @@ qt_feature("permissions" PUBLIC
SECTION "Utilities"
LABEL "Application permissions"
PURPOSE "Provides support for requesting user permission to access restricted data or APIs"
- CONDITION APPLE OR ANDROID OR WASM
+ CONDITION APPLE OR ANDROID OR WASM OR OHOS
)
qt_configure_add_summary_section(NAME "Qt Core")
qt_configure_add_summary_entry(ARGS "backtrace")
@@ -1854,6 +1854,38 @@ static bool android_default_message_handler(QtMsgType type,
}
#endif //Q_OS_ANDROID
+#ifdef Q_OS_OPENHARMONY
+#include <hilog/log.h>
+#define APP_LOG_DOMAIN 0xf000
+#define APP_LOG_TAG "QtForOpenHarmony"
+
+static bool openharmony_default_message_handler(QtMsgType type,
+ const QMessageLogContext &context,
+ const QString &message)
+{
+#if 0
+ if (shouldLogToStderr())
+ return false; // Leave logging up to stderr handler
+#endif
+
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+
+ LogLevel priority = LOG_INFO;
+ switch (type) {
+ //LOG_DEBUG unable to print, temporarily use LOG_ INFO replace.
+ case QtDebugMsg: priority = LOG_INFO; break;
+ case QtInfoMsg: priority = LOG_INFO; break;
+ case QtWarningMsg: priority = LOG_WARN; break;
+ case QtCriticalMsg: priority = LOG_ERROR; break;
+ case QtFatalMsg: priority = LOG_FATAL; break;
+ };
+
+ OH_LOG_Print(LOG_APP, priority, APP_LOG_DOMAIN, APP_LOG_TAG, "%{public}s %{public}s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(formattedMessage));
+
+ return true; // Prevent further output to stderr
+}
+#endif
+
#ifdef Q_OS_WIN
static void win_outputDebugString_helper(const QString &message)
{
@@ -1965,6 +1997,8 @@ static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &con
handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
# elif defined Q_OS_WASM
handledStderr |= wasm_default_message_handler(type, context, message);
+#elif defined Q_OS_OPENHARMONY
+ handledStderr |= openharmony_default_message_handler(type, context, message);
# endif
#endif
@@ -5,6 +5,13 @@
#ifndef QLOGGING_H
#define QLOGGING_H
+#ifdef Q_OS_OPENHARMONY
+#include <cstdarg>
+#include <array>
+#include <cstring>
+#include <stdio.h>
+#include <hitrace/trace.h>
+#endif
#if 0
// header is automatically included in qglobal.h
@@ -190,5 +197,29 @@ Q_CORE_EXPORT QString qFormatLogMessage(QtMsgType type, const QMessageLogContext
Q_DECL_COLD_FUNCTION
Q_CORE_EXPORT QString qt_error_string(int errorCode = -1);
+#ifdef Q_OS_OPENHARMONY
+class HiTracer {
+public:
+ HiTracer(const char *format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ std::array<char, 256> msgBuffer;
+ if (vsnprintf(msgBuffer.data(), msgBuffer.size(), format, ap) < 0) {
+ snprintf(msgBuffer.data(), msgBuffer.size(), "[error formatting label: %s]", format);
+ }
+ OH_HiTrace_StartTrace(msgBuffer.data());
+ va_end(ap);
+ }
+
+ ~HiTracer()
+ {
+ OH_HiTrace_FinishTrace();
+ }
+
+ HiTracer(const HiTracer&) = delete;
+ HiTracer& operator=(const HiTracer&) = delete;
+};
+#endif
QT_END_NAMESPACE
#endif // QLOGGING_H
@@ -215,6 +215,7 @@ namespace Qt {
SubWindow = 0x00000012,
ForeignWindow = 0x00000020 | Window,
CoverWindow = 0x00000040 | Window,
+ Floating = 0x00000080 | Window,
WindowType_Mask = 0x000000ff,
MSWindowsFixedSizeDialogHint = 0x00000100,
@@ -126,7 +126,7 @@ QOperatingSystemVersionBase QOperatingSystemVersionBase::current()
return v;
}
-#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN)
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN) && !defined(Q_OS_OPENHARMONY)
QOperatingSystemVersionBase QOperatingSystemVersionBase::current_impl()
{
QOperatingSystemVersionBase version;
@@ -336,6 +336,8 @@ QString QOperatingSystemVersionBase::name(QOperatingSystemVersionBase osversion)
return QStringLiteral("watchOS");
case QOperatingSystemVersionBase::Android:
return QStringLiteral("Android");
+ case QOperatingSystemVersionBase::OpenHarmony:
+ return QStringLiteral("OpenHarmony");
case QOperatingSystemVersionBase::Unknown:
default:
return QString();
@@ -681,6 +683,7 @@ const QOperatingSystemVersion QOperatingSystemVersion::Android10 =
const QOperatingSystemVersion QOperatingSystemVersion::Android11 =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 11, 0);
+
/*!
\variable QOperatingSystemVersion::Android12
\brief a version corresponding to Android 12 (version 12.0, API level 31).
@@ -702,6 +705,22 @@ const QOperatingSystemVersionBase QOperatingSystemVersion::Android12L;
*/
const QOperatingSystemVersionBase QOperatingSystemVersion::Android13;
+/*!
+ \variable QOperatingSystemVersion::OpenHarmony4
+ \brief a version corresponding to OpenHarmony 4 (version 4.0).
+ \since 6.5.5
+ */
+const QOperatingSystemVersion QOperatingSystemVersion::OpenHarmony4 =
+ QOperatingSystemVersion(QOperatingSystemVersion::OpenHarmony, 4, 0);
+
+/*!
+ \variable QOperatingSystemVersion::OpenHarmony5
+ \brief a version corresponding to OpenHarmony 5 (version 5.0).
+ \since 6.5.5
+ */
+const QOperatingSystemVersion QOperatingSystemVersion::OpenHarmony5 =
+ QOperatingSystemVersion(QOperatingSystemVersion::OpenHarmony, 5, 0);
+
#endif // !QT_BOOTSTRAPPED
#ifndef QT_NO_DEBUG_STREAM
@@ -23,7 +23,8 @@ public:
IOS,
TvOS,
WatchOS,
- Android
+ Android,
+ OpenHarmony
};
constexpr QOperatingSystemVersionBase(OSType osType,
@@ -52,6 +53,8 @@ public:
return WatchOS;
#elif defined(Q_OS_ANDROID)
return Android;
+#elif defined(Q_OS_OPENHARMONY)
+ return OpenHarmony;
#else
return Unknown;
#endif
@@ -113,7 +116,8 @@ public:
IOS,
TvOS,
WatchOS,
- Android
+ Android,
+ OpenHarmony
};
#endif
@@ -152,6 +156,8 @@ public:
static const QOperatingSystemVersion AndroidPie;
static const QOperatingSystemVersion Android10;
static const QOperatingSystemVersion Android11;
+ static const QOperatingSystemVersion OpenHarmony4;
+ static const QOperatingSystemVersion OpenHarmony5;
#else
static constexpr QOperatingSystemVersionBase Windows7 { QOperatingSystemVersionBase::Windows, 6, 1 };
static constexpr QOperatingSystemVersionBase Windows8 { QOperatingSystemVersionBase::Windows, 6, 2 };
@@ -182,6 +188,8 @@ public:
static constexpr QOperatingSystemVersionBase AndroidPie { QOperatingSystemVersionBase::Android, 9, 0 };
static constexpr QOperatingSystemVersionBase Android10 { QOperatingSystemVersionBase::Android, 10, 0 };
static constexpr QOperatingSystemVersionBase Android11 { QOperatingSystemVersionBase::Android, 11, 0 };
+ static constexpr QOperatingSystemVersionBase OpenHarmony15 { QOperatingSystemVersionBase::OpenHarmony, 4, 0 };
+ static constexpr QOperatingSystemVersionBase OpenHarmony16 { QOperatingSystemVersionBase::OpenHarmony, 5, 0 };
#endif // New (static constexpr) entries go here, only cherry-pick as far back as 6.3 (QTBUG-97808):
static constexpr QOperatingSystemVersionBase Windows10_1809 { QOperatingSystemVersionBase::Windows, 10, 0, 17763 }; // RS5
new file mode 100644
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qoperatingsystemversion.h"
+#include "qohutility.h"
+
+#include <QVersionNumber>
+
+QT_BEGIN_NAMESPACE
+
+QOperatingSystemVersionBase QOperatingSystemVersionBase::current_impl()
+{
+ QOperatingSystemVersionBase version;
+ version.m_os = currentType();
+ const QVersionNumber v = QVersionNumber::fromString(QtOh::Utility::distributionOSVersion());
+ if (!v.isNull()) {
+ version.m_major = v.majorVersion();
+ version.m_minor = v.minorVersion();
+ version.m_micro = v.microVersion();
+ return version;
+ }
+ version.m_major = -1;
+ version.m_minor = -1;
+ version.m_micro = -1;
+ return version;
+}
+
+QT_END_NAMESPACE
@@ -474,6 +474,13 @@ static const char *osVer_helper(QOperatingSystemVersion)
}
#endif
+#ifdef Q_OS_OPENHARMONY
+static const char *osVer_helper(QOperatingSystemVersion)
+{
+ return "";
+}
+#endif
+
/*!
\since 5.4
@@ -779,6 +786,9 @@ QString QSysInfo::productType()
#elif defined(Q_OS_ANDROID)
return QStringLiteral("android");
+#elif defined(Q_OS_OPENHARMONY)
+ return QStringLiteral("openharmony");
+
#elif defined(Q_OS_IOS)
return QStringLiteral("ios");
#elif defined(Q_OS_TVOS)
@@ -838,6 +848,9 @@ QString QSysInfo::productVersion()
#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN)
const auto version = QOperatingSystemVersion::current();
return QString::asprintf("%d.%d", version.majorVersion(), version.minorVersion());
+#elif defined(Q_OS_OPENHARMONY)
+ const auto version = QOperatingSystemVersion::current();
+ return QString::asprintf("%d.%d.%d", version.majorVersion(), version.minorVersion(), version.microVersion());
#elif defined(Q_OS_WIN)
const char *version = osVer_helper();
if (version) {
@@ -873,7 +886,7 @@ QString QSysInfo::productVersion()
*/
QString QSysInfo::prettyProductName()
{
-#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
const auto version = QOperatingSystemVersion::current();
const int majorVersion = version.majorVersion();
const QString versionString = QString::asprintf("%d.%d", majorVersion, version.minorVersion());
@@ -88,6 +88,9 @@
#elif defined(__ANDROID__) || defined(ANDROID)
# define Q_OS_ANDROID
# define Q_OS_LINUX
+#elif defined(__OHOS__) || defined(OPENHARMONY)
+# define Q_OS_OPENHARMONY
+# define Q_OS_LINUX
#elif defined(__CYGWIN__)
# define Q_OS_CYGWIN
#elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
@@ -768,7 +768,7 @@ QFile::copy(const QString &newName)
d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
} else {
const auto fileTemplate = "%1/qt_temp.XXXXXX"_L1;
-#ifdef QT_NO_TEMPORARYFILE
+#if defined(QT_NO_TEMPORARYFILE) || defined(Q_OS_OPENHARMONY)
QFile out(fileTemplate.arg(QFileInfo(newName).path()));
if (!out.open(QIODevice::ReadWrite))
error = true;
@@ -820,7 +820,7 @@ QFile::copy(const QString &newName)
.arg(newName, out.errorString()));
}
}
-#ifdef QT_NO_TEMPORARYFILE
+#if defined(QT_NO_TEMPORARYFILE) || defined(Q_OS_OPENHARMONY)
if (error)
out.remove();
#else
@@ -165,6 +165,9 @@ static bool isLocalScheme(const QString &file)
bool local = file == "qrc"_L1;
#ifdef Q_OS_ANDROID
local |= file == "assets"_L1;
+#endif
+#ifdef Q_OS_OPENHARMONY
+ local |= file == QLatin1String("rawfile");
#endif
return local;
}
@@ -189,7 +192,10 @@ QUrl QFileSelector::select(const QUrl &filePath) const
if (filePath.scheme() == "assets"_L1)
scheme = "assets:"_L1;
#endif
-
+#ifdef Q_OS_OPENHARMONY
+ if (filePath.scheme() == QLatin1String("rawfile"))
+ scheme = QLatin1String("rawfile:");
+#endif
QString equivalentPath = scheme + filePath.path();
QString selectedPath = d->select(equivalentPath);
ret.setPath(selectedPath.remove(0, scheme.size()));
@@ -72,6 +72,15 @@ extern "C" NSString *NSTemporaryDirectory();
# undef STATX_BASIC_STATS
#endif
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 17
+#include "qjsmodule.h"
+#include "private/qopenharmony_p.h"
+#include "private/qjspromise_p.h"
+#include <filemanagement/file_uri/oh_file_uri.h>
+#endif
+#endif
+
#ifndef STATX_ALL
struct statx { mode_t stx_mode; }; // dummy
#endif
@@ -1275,7 +1284,29 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
return false;
}
const QString sourcePath = sourceInfo.absoluteFilePath();
+#if defined(Q_OS_OPENHARMONY) && (OHOS_SDK_VERSION >= 17)
+ if (qApp && !qApp->inherits("QGuiApplication")) {
+ error = QSystemError(ENOENT, QSystemError::NativeError);
+ return false;
+ }
+ QString targetPath = QtOh::runOnJsUIThreadWithPromise<QString>([](auto p, const QString &filePath){
+ static QJsModule fileManagerService(QString::fromUtf8("@hms.filemanagement.fileManagerService"));
+ QString uri = QtOh::uriFromPath(filePath);
+ if (uri.isEmpty()) {
+ p->set_value(QString());
+ return;
+ }
+ Napi::Promise result = fileManagerService.call(QString::fromUtf8("deleteToTrash"), {Napi::Value::From(fileManagerService.env(), uri.toStdString())}).As<Napi::Promise>();
+ QJsPromise promise(result);
+ promise.onThen([p](const Napi::CallbackInfo &info){
+ QString fileName = QString::fromStdString(info[0].ToString());
+ p->set_value(QtOh::pathFromUri(fileName));
+ }).onCatch([p](const Napi::CallbackInfo &info){
+ p->set_value(QString());
+ });
+ }, sourcePath);
+#else
QDir trashDir(freeDesktopTrashLocation(sourcePath));
if (!trashDir.exists())
return false;
@@ -1363,7 +1394,7 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
+ "\n";
infoFile.write(info);
infoFile.close();
-
+#endif
newLocation = QFileSystemEntry(targetPath);
return true;
#endif // QT_BOOTSTRAPPED
@@ -272,7 +272,7 @@ QString QSettingsPrivate::normalizedKey(QAnyStringView key)
// see also qsettings_win.cpp and qsettings_mac.cpp
-#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM) && !defined(Q_OS_OPENHARMONY)
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
const QString &organization, const QString &application)
{
@@ -280,7 +280,7 @@ QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::
}
#endif
-#if !defined(Q_OS_WIN)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_OPENHARMONY)
QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
{
return new QConfFileSettingsPrivate(fileName, format);
@@ -1432,7 +1432,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
ensureAllSectionsParsed(confFile);
ParsedSettingsMap mergedKeys = confFile->mergedKeyMap();
-#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) && !defined(Q_OS_OPENHARMONY)
QSaveFile sf(confFile->name);
sf.setDirectWriteFallback(!atomicSyncOnly);
# ifdef Q_OS_ANDROID
@@ -1466,7 +1466,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
ok = writeFunc(sf, tempOriginalKeys);
}
-#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile) && !defined(Q_OS_OPENHARMONY)
if (ok)
ok = sf.commit();
#endif
new file mode 100644
@@ -0,0 +1,778 @@
+#include "qsettings.h"
+#include "qsettings_p.h"
+#include <asset/asset_api.h>
+// #include <QDebug>
+#include <QCborValue>
+QT_BEGIN_NAMESPACE
+
+/**
+ * @brief 官方API约束限制
+ * @note 批量查询出的关键资产需要通过IPC通道传输给业务,受IPC缓冲区大小限制,
+ * 建议对查询超过40条关键资产时,进行分批查询,且每次查询数量不超过40条
+ *
+ * @see https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/asset-native-query-V5
+ */
+constexpr Asset_Attr GlobalReturnLimitAttr = { ASSET_TAG_RETURN_LIMIT, { .u32 = 40 } };
+constexpr char GlobalLabel[] = "QOhAssetStore";
+const Asset_Attr GlobalAssetStoreLabelAttr = { ASSET_TAG_DATA_LABEL_NORMAL_1,
+ { .blob = {static_cast<quint32>(strlen(GlobalLabel)), reinterpret_cast<quint8 *>(const_cast<char *>(GlobalLabel))} }
+};
+
+static Asset_ResultCode retCode = ASSET_SUCCESS;
+using AssetResultSetCallback = std::function<void(const Asset_ResultSet&)>;
+
+class QOhAssetStoreHelper {
+
+public:
+
+public:
+ // class iterator {
+ // public:
+ // using value_type = QPair<QString, QVariant>;
+ // explicit iterator(int idx = 0);
+ // value_type operator*() const;
+ // QString key() const;
+ // QVariant value() const;
+ // iterator &operator++();
+ // bool operator!=(const iterator &) const;
+ // private:
+ // QStringList m_keys = {};
+ // int m_index = -1;
+ // };
+
+ // class reference : public QVariant {
+ // public:
+ // explicit reference(const QString &);
+ // reference &operator=(const QVariant &);
+ // private:
+ // QString m_key = QString();
+ // };
+
+ // iterator begin() const;
+ // iterator end() const;
+
+ // reference operator[](const QString &key);
+ // QVariant operator[](const QString &key) const;
+
+ QOhAssetStoreHelper(const QOhAssetStoreHelper &) = delete;
+ QOhAssetStoreHelper &operator=(const QOhAssetStoreHelper &) = delete;
+
+ QOhAssetStoreHelper(QOhAssetStoreHelper &&) = delete;
+ QOhAssetStoreHelper &operator=(QOhAssetStoreHelper &&) = delete;
+
+ /**
+ * @brief instance
+ * @return QOhAssetStoreHelper 的单例实例
+ */
+ static QOhAssetStoreHelper &instance();
+ /**
+ * @brief insert 插入或更新一个别名和对应的秘密数据
+ * @param alias 关键资产别名,每条关键资产的唯一索引
+ * @param secret 关键资产明文,可以是任意 QVariant 类型
+ * @return 成功插入或更新返回 true,否则返回 false
+ */
+ bool insert(const QString &alias, const QVariant &secret);
+ /**
+ * @brief remove 从存储中移除指定别名的资产
+ * @param alias 关键资产别名
+ * @return 成功移除返回 true,否则返回 false
+ */
+ bool remove(const QString &alias);
+ /**
+ * @brief contains 检查存储中是否存在指定别名的资产
+ * @param alias 关键资产别名
+ * @return 存在返回 true,否则返回 false
+ */
+ bool contains(const QString &alias) const;
+ /**
+ * @brief value 获取指定别名的资产值
+ * @param alias 关键资产别名
+ * @return 资产值,如果不存在则返回无效 QVariant
+ */
+ QVariant value(const QString &alias) const;
+ /**
+ * @brief value 获取指定别名的资产值,如果不存在则返回默认值
+ * @param alias 关键资产别名
+ * @param defaultValue 默认值
+ * @return 资产值,如果不存在则返回 defaultValue
+ */
+ QVariant value(const QString &alias, const QVariant &defaultValue) const;
+ /**
+ * @brief keys 获取存储中所有资产的别名列表
+ * @return 别名列表
+ */
+ QStringList keys() const;
+ /**
+ * @brief size 获取存储中资产的数量
+ * @return 资产数量
+ */
+ int size() const;
+ /**
+ * @brief clear 清空存储中的所有资产
+ * @return 成功清空返回 true,否则返回 false
+ */
+ bool clear();
+ /**
+ * @brief lastError 获取最近一次操作的错误信息
+ * @return 错误信息字符串,如果没有错误则返回空字符串
+ */
+ QString lastError() const;
+
+private:
+ QOhAssetStoreHelper();
+};
+/**
+ * @brief deserialize 将序列化字符串反序列化为 QVariant
+ * @param str 序列化的字符串
+ * @return 反序列化后的 QVariant
+ */
+static QVariant deserialize(const QByteArray &byteArray)
+{
+ if (byteArray.isEmpty()) return QVariant();
+ return QCborValue::fromCbor(byteArray).toVariant();
+}
+
+/**
+ * @brief serialize 将 QVariant 序列化为 QByteArray 字符串
+ * @param value 待序列化的 QVariant
+ * @return 序列化字符串
+ */
+static QByteArray serialize(const QVariant &value)
+{
+ if (!value.isValid()) return QByteArray();
+ return QCborValue::fromVariant(value).toCbor();
+}
+/**
+ * @brief createBlob 创建一个 Asset_Blob 对象
+ * @param data 待转换的 QByteArray 数据
+ * @return 返回创建的 Asset_Blob 对象
+ */
+static Asset_Blob createBlob(const QByteArray &data)
+{
+ return Asset_Blob{static_cast<quint32>(data.size()), reinterpret_cast<quint8 *>(const_cast<char *>(data.data()))};
+}
+/**
+ * @brief blobToByteArray 将 Asset_Blob 转换为 QByteArray
+ * @param blob 待转换的 Asset_Blob 对象
+ * @return 返回转换后的 QByteArray,如果 blob 为空则返回空 QByteArray
+ */
+static QByteArray blobToByteArray(const Asset_Blob &blob)
+{
+ if (!blob.data || blob.size == 0) return QByteArray();
+ return QByteArray(reinterpret_cast<const char *>(blob.data), blob.size);
+}
+/**
+ * @brief removeAsset 根据指定的过滤条件和标签从存储中移除资产
+ * @param filter 过滤条件字符串
+ * @param filterTag 过滤标签
+ * @return 成功移除返回 true,否则返回 false
+ */
+static bool removeAsset(const QString &filter, const quint32 &filterTag)
+{
+ const int count = 1;
+ Asset_Attr attrs[count];
+ QByteArray filterByte = filter.toUtf8();
+ attrs[0] = { filterTag, { .blob = createBlob(filterByte) } };
+ retCode = (Asset_ResultCode)OH_Asset_Remove(attrs, count);
+ return retCode == ASSET_SUCCESS;
+}
+/**
+ * @brief queryAsset 根据别名查询资产,并通过回调函数返回结果集
+ * @param alias 资产别名
+ * @param callback 查询结果的回调函数
+ * @param retType 返回类型,决定返回的属性集
+ */
+static void queryAsset(const QString &alias, AssetResultSetCallback callback, const quint32 &retType)
+{
+ const int count = 2;
+ Asset_Attr filter[count];
+ QByteArray aliasByte = alias.toUtf8();
+ filter[0] = { ASSET_TAG_ALIAS, { .blob = createBlob(aliasByte) } };
+ filter[1] = { ASSET_TAG_RETURN_TYPE, { .u32 = retType } };
+
+ Asset_ResultSet rs = {0};
+ if (retCode = (Asset_ResultCode)OH_Asset_Query(filter, count, &rs); retCode == ASSET_SUCCESS && rs.count > 0) {
+ callback(rs);
+ }
+ OH_Asset_FreeResultSet(&rs);
+}
+/**
+ * @brief batchQuery 批量查询资产,并通过回调函数返回结果集
+ * @param callback 查询结果的回调函数
+ * @param retType 返回类型,决定返回的属性集
+ */
+static void batchQuery(AssetResultSetCallback callback, const quint32 &retType)
+{
+ quint32 offset = 0;
+ /*
+ * filter by ASSET_TAG_DATA_LABEL_NORMAL_1 with pagination
+ */
+ const int count = 5;
+ while (true) {
+ Asset_Attr filter[count];
+ filter[0] = GlobalAssetStoreLabelAttr;
+ filter[1] = { ASSET_TAG_RETURN_TYPE, { .u32 = retType } };
+ filter[2] = GlobalReturnLimitAttr;
+ filter[3] = { ASSET_TAG_RETURN_OFFSET, { .u32 = offset } };
+ filter[4] = { ASSET_TAG_RETURN_ORDERED_BY, { .u32 = ASSET_TAG_DATA_LABEL_NORMAL_1 } };
+
+ Asset_ResultSet rs = {0};
+
+ if (retCode = (Asset_ResultCode)OH_Asset_Query(filter, count, &rs); retCode == ASSET_SUCCESS && rs.count > 0) {
+ callback(rs);
+ OH_Asset_FreeResultSet(&rs);
+ if (rs.count < GlobalReturnLimitAttr.value.u32) break;
+ offset += GlobalReturnLimitAttr.value.u32;
+ continue;
+ }
+ else {
+ OH_Asset_FreeResultSet(&rs);
+ break;
+ }
+ }
+}
+/**
+ * @brief qohAssetGetBlob 从查询结果中获取指定标签的 Blob 数据
+ * @param result 查询结果集
+ * @param tag 指定的标签
+ * @return 返回对应标签的 Blob 数据,如果未找到则返回空 QByteArray
+ */
+static QByteArray qohAssetGetBlob(const Asset_Result *result, const Asset_Tag tag)
+{
+ if (auto *assetAttr = OH_Asset_ParseAttr(result, tag)) return blobToByteArray(assetAttr->value.blob);
+ return QByteArray();
+}
+
+QOhAssetStoreHelper::QOhAssetStoreHelper() {}
+
+// QOhAssetStoreHelper::iterator QOhAssetStoreHelper::begin() const { return iterator(); }
+
+// QOhAssetStoreHelper::iterator QOhAssetStoreHelper::end() const { return iterator(-1); }
+
+
+// QOhAssetStoreHelper::reference QOhAssetStoreHelper::operator[](const QString &key) {
+// return reference(key);
+// }
+
+// QVariant QOhAssetStoreHelper::operator[](const QString &key) const {
+// return reference(key);
+// }
+
+QOhAssetStoreHelper &QOhAssetStoreHelper::instance()
+{
+ static QOhAssetStoreHelper s_instance;
+ return s_instance;
+}
+
+bool QOhAssetStoreHelper::insert(const QString &alias, const QVariant &secret)
+{
+ QByteArray secreByte = serialize(secret);
+ QByteArray aliasByte = alias.toUtf8();
+
+ const uint32_t count = 4;
+ Asset_Attr attrs[count];
+ attrs[0] = GlobalAssetStoreLabelAttr;
+ attrs[1] = { ASSET_TAG_CONFLICT_RESOLUTION, { .u32 = ASSET_CONFLICT_OVERWRITE } };
+ attrs[2] = { ASSET_TAG_ALIAS, { .blob = createBlob(aliasByte) } };
+ attrs[3] = { ASSET_TAG_SECRET, { .blob = createBlob(secreByte) } };
+
+ int32_t code = OH_Asset_Add(attrs, sizeof(attrs) / sizeof(attrs[0]));
+ retCode = (Asset_ResultCode)(code);
+ return retCode == ASSET_SUCCESS;
+}
+
+bool QOhAssetStoreHelper::remove(const QString &alias)
+{
+ return removeAsset(alias, ASSET_TAG_ALIAS);
+}
+
+bool QOhAssetStoreHelper::contains(const QString &alias) const
+{
+ bool found = false;
+ queryAsset(alias, [&found](const Asset_ResultSet &rs) { found = rs.count > 0; }, ASSET_RETURN_ATTRIBUTES);
+ return found;
+}
+
+QVariant QOhAssetStoreHelper::value(const QString &alias) const
+{
+ QVariant secret;
+ queryAsset(alias, [&secret](const auto &rs) {
+ secret = deserialize(qohAssetGetBlob(rs.results, ASSET_TAG_SECRET));
+ }, ASSET_RETURN_ALL);
+ return secret;
+}
+
+QVariant QOhAssetStoreHelper::value(const QString &alias, const QVariant &defaultValue) const
+{
+ if (QVariant secret = value(alias); secret.isValid()) return secret;
+ return defaultValue;
+}
+
+QStringList QOhAssetStoreHelper::keys() const
+{
+ QStringList list;
+ batchQuery([&list](const auto &rs) {
+ for (quint32 i = 0; i < rs.count; ++i)
+ list.append(qohAssetGetBlob(rs.results + i, ASSET_TAG_ALIAS));
+ }, ASSET_RETURN_ATTRIBUTES);
+ return list;
+}
+
+int QOhAssetStoreHelper::size() const
+{
+ int total = 0;
+ batchQuery([&total](const auto &rs) {total += rs.count;}, ASSET_RETURN_ATTRIBUTES);
+ return total;
+}
+
+bool QOhAssetStoreHelper::clear()
+{
+ return removeAsset(GlobalLabel, ASSET_TAG_DATA_LABEL_NORMAL_1);
+}
+
+QString QOhAssetStoreHelper::lastError() const
+{
+ switch (retCode) {
+ case ASSET_SUCCESS:
+ return QString();
+ case ASSET_PERMISSION_DENIED:
+ return QStringLiteral("The caller doesn't have the permission.");
+ case ASSET_INVALID_ARGUMENT:
+ return QStringLiteral("The parameter is invalid.");
+ case ASSET_SERVICE_UNAVAILABLE:
+ return QStringLiteral("The ASSET service is unavailable.");
+ case ASSET_NOT_FOUND:
+ return QStringLiteral("The asset is not found.");
+ case ASSET_DUPLICATED:
+ return QStringLiteral("The asset already exists.");
+ case ASSET_ACCESS_DENIED:
+ return QStringLiteral("Access to the asset is denied.");
+ case ASSET_STATUS_MISMATCH:
+ return QStringLiteral("The status of the asset does not match.");
+ case ASSET_OUT_OF_MEMORY:
+ return QStringLiteral("The system runs out of memory.");
+ case ASSET_DATA_CORRUPTED:
+ return QStringLiteral("The asset data is corrupted.");
+ case ASSET_DATABASE_ERROR:
+ return QStringLiteral("The database operation failed.");
+ case ASSET_CRYPTO_ERROR:
+ return QStringLiteral("The cryptography operation failed.");
+ case ASSET_IPC_ERROR:
+ return QStringLiteral("IPC failed.");
+ case ASSET_BMS_ERROR:
+ return QStringLiteral("Calling the Bundle Manager service failed.");
+ case ASSET_ACCOUNT_ERROR:
+ return QStringLiteral("Calling the OS Account service failed.");
+ case ASSET_ACCESS_TOKEN_ERROR:
+ return QStringLiteral("Calling the Access Token service failed.");
+ case ASSET_FILE_OPERATION_ERROR:
+ return QStringLiteral("The file operation failed.");
+ case ASSET_GET_SYSTEM_TIME_ERROR:
+ return QStringLiteral("Getting the system time failed.");
+ case ASSET_LIMIT_EXCEEDED:
+ return QStringLiteral("The cache exceeds the limit.");
+ case ASSET_UNSUPPORTED:
+ return QStringLiteral("The capability is not supported.");
+ }
+}
+
+// QOhAssetStoreHelper::iterator::iterator(int idx)
+// : m_keys(QOhAssetStoreHelper::instance().keys()), m_index(idx)
+// {
+// if (m_index < 0)
+// m_index = m_keys.size();
+// }
+
+// QOhAssetStoreHelper::iterator::value_type QOhAssetStoreHelper::iterator::operator*() const {
+// const QString &k = m_keys[m_index];
+// return { k, QOhAssetStoreHelper::instance().value(k) };
+// }
+
+// QString QOhAssetStoreHelper::iterator::key() const {
+// return m_keys[m_index];
+// }
+
+// QVariant QOhAssetStoreHelper::iterator::value() const {
+// return QOhAssetStoreHelper::instance().value(m_keys[m_index]);
+// }
+
+// QOhAssetStoreHelper::iterator &QOhAssetStoreHelper::iterator::operator++() {
+// ++m_index;
+// return *this;
+// }
+
+// bool QOhAssetStoreHelper::iterator::operator!=(const iterator &other) const {
+// return m_index != other.m_index;
+// }
+
+// QOhAssetStoreHelper::reference::reference(const QString &k)
+// : m_key(k)
+// {
+// setValue(QOhAssetStoreHelper::instance().value(m_key, QVariant()));
+// }
+
+// QOhAssetStoreHelper::reference &QOhAssetStoreHelper::reference::operator=(const QVariant &v) {
+// if(QOhAssetStoreHelper::instance().insert(m_key, v)) setValue(v);
+// return *this;
+// }
+
+/*
+ * 使用QHash<QString, QVariant>最小化的QSettings实现,仅用于通过基础测试,后切换到 QOhAssetStoreHelper
+ */
+#define USE_ASSET_STORE
+#ifdef USE_ASSET_STORE
+static auto &globalStorage = QOhAssetStoreHelper::instance();
+#else
+static QHash<QString, QVariant> globalStorage;
+#endif
+
+class QOhSettingsPrivate : public QSettingsPrivate
+{
+public:
+ QOhSettingsPrivate(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application);
+ QOhSettingsPrivate(const QString &rKey);
+ ~QOhSettingsPrivate();
+
+ void remove(const QString &key) override;
+ void set(const QString &key, const QVariant &value) override;
+ std::optional<QVariant> get(const QString &key) const override;
+ QStringList children(const QString &prefix, ChildSpec spec) const override;
+ void clear() override;
+ void sync() override;
+ void flush() override;
+ bool isWritable() const override;
+ QString fileName() const override;
+
+private:
+ QString qSettingsInit;
+};
+
+QOhSettingsPrivate::QOhSettingsPrivate(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application)
+ : QSettingsPrivate(format, scope, organization, application)
+{
+ if (organization.isEmpty()) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
+ qSettingsInit = organization;
+ if (!application.isEmpty()) {
+ qSettingsInit += "/" + application;
+ }
+
+ switch (scope) {
+ case QSettings::UserScope:
+ qSettingsInit = QString::fromLatin1("QOhSettings.User/") + qSettingsInit;
+ break;
+ case QSettings::SystemScope:
+ qSettingsInit = QString::fromLatin1("QOhSettings.System/") + qSettingsInit;
+ break;
+ }
+ /*
+ * 调试信息
+ */
+ // qDebug() << "QOhSettingsPrivate constructor1 - qSettingsInit:" << qSettingsInit;
+}
+
+QOhSettingsPrivate::QOhSettingsPrivate(const QString &rKey)
+ : QSettingsPrivate(QSettings::NativeFormat)
+{
+ if (!rKey.isEmpty()) {
+ /*
+ * 对于OpenHarmony,fileName就是我们的qSettingsInit
+ * 确保使用相同的键格式,以便不同构造方式的实例能共享数据
+ */
+ qSettingsInit = rKey;
+ } else {
+ setStatus(QSettings::AccessError);
+ }
+ /*
+ * 调试信息
+ */
+ // qDebug() << "QOhSettingsPrivate constructor2 - qSettingsInit:" << qSettingsInit;
+}
+
+QOhSettingsPrivate::~QOhSettingsPrivate()
+{
+
+}
+
+void QOhSettingsPrivate::remove(const QString &uKey)
+{
+ if (uKey.isEmpty()) {
+ return;
+ }
+ /*
+ * 构建完整的键,不要重复添加groupPrefix,因为QSettings已经处理了
+ */
+ QString fullKey = qSettingsInit + "/" + uKey;
+ /*
+ * 删除指定的键
+ */
+ globalStorage.remove(fullKey);
+ /*
+ * 删除所有以该键为前缀的子键
+ */
+ QString keyPrefix = fullKey + "/";
+ // QStringList keysToRemove;
+
+ auto keys = globalStorage.keys();
+ for(const auto &key : keys) {
+ if (key.startsWith(keyPrefix)) {
+ // keysToRemove << key;
+ globalStorage.remove(key);
+ }
+ }
+ // for (auto it = globalStorage.begin(); it != globalStorage.end(); ++it) {
+ // if (it.key().startsWith(keyPrefix)) {
+ // keysToRemove << it.key();
+ // }
+ // }
+
+ // for (const QString &key : keysToRemove) {
+ // globalStorage.remove(key);
+ // }
+
+ /*
+ * 调试信息
+ */
+ // qDebug() << "QOhSettingsPrivate::remove - removed key:" << fullKey << "and" << keysToRemove.size() << "subkeys" << "groupPrefix:" << groupPrefix;
+}
+
+void QOhSettingsPrivate::set(const QString &uKey, const QVariant &value)
+{
+ if (uKey.isEmpty()) {
+ return;
+ }
+
+ if (!value.isValid()) {
+ remove(uKey);
+ return;
+ }
+ /*
+ * 构建完整的键,不要重复添加groupPrefix,因为QSettings已经处理了
+ */
+ QString fullKey = qSettingsInit + "/" + uKey;
+
+ // globalStorage[fullKey] = value;
+ globalStorage.insert(fullKey, value);
+
+ /*
+ * 调试信息
+ */
+ // qDebug() << "QOhSettingsPrivate::set - qSettingsInit:" << qSettingsInit << "stored key:" << fullKey << "value:" << value;
+}
+
+std::optional<QVariant> QOhSettingsPrivate::get(const QString &uKey) const
+{
+ std::optional<QVariant> result;
+ if (uKey.isEmpty() ) {
+ return result;
+ }
+
+ /*
+ * 构建完整的键,不要重复添加groupPrefix,因为QSettings已经处理了
+ */
+ QString fullKey = qSettingsInit + "/" + uKey;
+ auto getOrFind = [&result](const QString &key) -> bool {
+ if (const auto va = globalStorage.value(key); va.isValid()) {
+ result = va;
+ return true;
+ } else {
+ if(globalStorage.contains(key))
+ return true;
+ }
+ return false;
+ };
+ /*
+ * 首先尝试直接查找
+ */
+ if (bool ret = getOrFind(fullKey)) return ret;
+ /*
+ * 只有在启用fallback时才进行fallback查找
+ */
+ if (!fallbacks) {
+ return false;
+ }
+ /*
+ * 优先级1: 同一scope内的no-app fallback (最高优先级)
+ */
+ QStringList parts = fullKey.split("/");
+ if (parts.size() >= 4) { // scope / organization / application / key
+ QString sameScope = parts[0]; // QOhSettings.User / QOhSettings.System
+ QString noAppSameScopeKey = sameScope + "/" + parts[1] + "/" + parts.mid(3).join("/");
+ if (bool ret = getOrFind(noAppSameScopeKey)) return result;
+ }
+ /*
+ * 优先级2: 跨scope完全匹配 fallback (只对UserScope)
+ */
+ if (qSettingsInit.contains("QOhSettings.User")) {
+ QString fallbackKey = fullKey;
+ fallbackKey.replace(0, QString("QOhSettings.User").length(), "QOhSettings.System");
+ if (bool ret = getOrFind(fallbackKey)) return result;
+ /*
+ * 优先级3: 跨scope no-app fallback (最低优先级)
+ */
+ if (parts.size() >= 4) {
+ QString noAppCrossScopeKey = "QOhSettings.System/" + parts[1] + "/" + parts.mid(3).join("/");
+ if (bool ret = getOrFind(noAppCrossScopeKey)) return result;
+ }
+ }
+ /*
+ * 简化的fallback:如果当前键包含路径分隔符且没有组前缀,直接尝试
+ */
+ if (groupPrefix.isEmpty() && uKey.contains("/")) {
+ QString directKey = qSettingsInit + "/" + uKey;
+ if (bool ret = getOrFind(directKey)) return result;
+ }
+
+ return false;
+}
+
+QStringList QOhSettingsPrivate::children(const QString &uKey, ChildSpec spec) const
+{
+ /*
+ * uKey 实际上就是 groupPrefix,是完整的组路径
+ * 构建完整的前缀
+ */
+ QString prefix = qSettingsInit;
+ if (!uKey.isEmpty()) {
+ /*
+ * 移除 uKey 末尾的斜杠(如果有的话)
+ */
+ QString cleanKey = uKey;
+ if (cleanKey.endsWith("/")) {
+ cleanKey.chop(1);
+ }
+ prefix += "/" + cleanKey;
+ }
+ /*
+ * 确保前缀以"/"结尾,用于精确匹配
+ */
+ QString searchPrefix = prefix + "/";
+
+ QStringList result;
+ /*
+ * 扫描全局存储中的所有键
+ */
+ auto keys = globalStorage.keys();
+ for (const auto &key : keys) {
+ /*
+ * 检查键是否以我们的前缀开始,或者完全匹配前缀
+ */
+ bool isMatch = false;
+ QString relativePath;
+ if (key == prefix) {
+ /*
+ * 完全匹配前缀本身,跳过
+ */
+ continue;
+ } else if (key.startsWith(searchPrefix)) {
+ /*
+ * 键以"prefix/"开头
+ */
+ relativePath = key.mid(searchPrefix.length());
+ isMatch = true;
+ }
+
+ if (!isMatch || relativePath.isEmpty()) {
+ continue;
+ }
+ int slashPos = relativePath.indexOf('/');
+ switch (spec) {
+ /*
+ * 对于AllKeys,添加所有相对路径
+ */
+ case AllKeys: {
+ if (!result.contains(relativePath))
+ result << relativePath;
+ break;
+ }
+ /*
+ * 直接子键
+ */
+ case ChildKeys: {
+ if(slashPos == -1 && !result.contains(relativePath))
+ result << relativePath;
+ break;
+ }
+ /*
+ * 子组
+ */
+ case ChildGroups: {
+ QString groupName = relativePath.left(slashPos);
+ if (slashPos != -1 && !result.contains(groupName))
+ result << groupName;
+ break;
+ }
+ }
+ }
+
+ // result.sort();
+ /*
+ * 调试信息
+ */
+ // qDebug() << "QOhSettingsPrivate::children - qSettingsInit:" << qSettingsInit << "uKey:" << uKey << "prefix:" << prefix << "searchPrefix:" << searchPrefix << "spec:" << spec;
+ // qDebug() << "QOhSettingsPrivate::children - global storage contents:";
+ // for (auto it = globalStorage.begin(); it != globalStorage.end(); ++it) {
+ // qDebug() << " " << it.key() << " = " << it.value();
+ // }
+ // qDebug() << "QOhSettingsPrivate::children - result:" << result;
+
+ return result;
+}
+
+void QOhSettingsPrivate::clear()
+{
+ globalStorage.clear();
+}
+
+void QOhSettingsPrivate::sync()
+{
+ /*
+ * OpenHarmony QSettings实现不需要sync操作
+ */
+}
+
+void QOhSettingsPrivate::flush()
+{
+ /*
+ * OpenHarmony QSettings实现不需要flush操作
+ */
+}
+
+bool QOhSettingsPrivate::isWritable() const
+{
+ return true;
+}
+
+QString QOhSettingsPrivate::fileName() const
+{
+ return qSettingsInit;
+}
+
+QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application)
+{
+ switch (format) {
+ case QSettings::NativeFormat:
+ return new QOhSettingsPrivate(format, scope, organization, application);
+ default:
+ return new QConfFileSettingsPrivate(format, scope, organization, application);
+ }
+}
+
+#ifdef Q_OS_OPENHARMONY
+QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
+{
+ switch (format) {
+ case QSettings::NativeFormat:
+ return new QOhSettingsPrivate(fileName);
+ default:
+ return new QConfFileSettingsPrivate(fileName, format);
+ }
+}
+#endif
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,144 @@
+#include "qstandardpaths.h"
+
+#ifndef QT_NO_STANDARDPATHS
+
+#include <QtCore/qmap.h>
+#include <QtCore/qopenharmony.h>
+#include <QDir>
+#include <filemanagement/environment/oh_environment.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QMap<QString, QString> OpenHarmonyDirCache;
+Q_GLOBAL_STATIC(OpenHarmonyDirCache, openHarmonyDirCache)
+
+static QString testDir()
+{
+ return QStandardPaths::isTestModeEnabled() ? QLatin1String("/qttest")
+ : QLatin1String("");
+}
+
+/*
+ * Locations where applications can place persistent files it owns.
+ * E.g., /storage/org.app/Music
+ */
+static QString getDir(const char *directoryField = 0)
+{
+ QString &path = (*openHarmonyDirCache)[QString(QLatin1String("APPNAME_%1")).arg(QLatin1String(directoryField))];
+ if (!path.isEmpty())
+ return path;
+
+
+ QString id = QString::fromLatin1(directoryField);
+ QString result;
+ char *path_to_get = nullptr;
+ if (id == "DIRECTORY_DOCUMENTS") {
+ OH_Environment_GetUserDocumentDir(&path_to_get);
+ } else if (id == "DIRECTORY_DOWNLOADS") {
+ OH_Environment_GetUserDownloadDir(&path_to_get);
+ } else if (id == "DIRECTORY_DESKTOP") {
+ OH_Environment_GetUserDesktopDir(&path_to_get);
+ } else {
+ result = QtOh::dir(id);
+ }
+ if (path_to_get != nullptr) {
+ result = QString::fromUtf8(path_to_get);
+ free(path_to_get);
+ }
+
+ path = result;
+ return result;
+}
+
+QString QStandardPaths::writableLocation(StandardLocation type)
+{
+ switch (type) {
+ case QStandardPaths::MusicLocation:
+ return QDir::homePath() + "/Music";
+ case QStandardPaths::MoviesLocation:
+ return QDir::homePath() + "/Videos";
+ case QStandardPaths::PicturesLocation:
+ return QDir::homePath() + "/Images";
+ case QStandardPaths::DocumentsLocation:
+ return getDir("DIRECTORY_DOCUMENTS");
+ case QStandardPaths::DownloadLocation:
+ return getDir("DIRECTORY_DOWNLOADS");
+ case QStandardPaths::GenericConfigLocation:
+ case QStandardPaths::ConfigLocation:
+ case QStandardPaths::AppConfigLocation:
+ return getDir("DIRECTORY_CACHE") + testDir() + QLatin1String("/settings");
+ case QStandardPaths::GenericDataLocation:
+ return getDir("DIRECTORY_CACHE") + testDir();
+ case QStandardPaths::AppDataLocation:
+ case QStandardPaths::AppLocalDataLocation:
+ return getDir("DIRECTORY_FILES") + testDir();
+ case QStandardPaths::RuntimeLocation:
+ return getDir("DIRECTORY_CACHE");
+ case QStandardPaths::TempLocation:
+ return getDir("DIRECTORY_TEMP");
+ case QStandardPaths::GenericCacheLocation:
+ case QStandardPaths::CacheLocation:
+ return getDir("DIRECTORY_CACHE");
+ case QStandardPaths::DesktopLocation:
+ return QDir::homePath() + "/Desktop";
+ case QStandardPaths::HomeLocation:
+ return QDir::homePath();
+ case QStandardPaths::ApplicationsLocation:
+ case QStandardPaths::FontsLocation:
+ default:
+ break;
+ }
+
+ return QString();
+}
+
+QStringList QStandardPaths::standardLocations(StandardLocation type)
+{
+ if (type == MusicLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == MoviesLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == PicturesLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == DocumentsLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == DownloadLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == AppDataLocation || type == AppLocalDataLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == CacheLocation) {
+ return QStringList() << writableLocation(type);
+ }
+
+ if (type == FontsLocation) {
+ QString &fontLocation = (*openHarmonyDirCache)[QStringLiteral("FONT_LOCATION")];
+ if (!fontLocation.isEmpty())
+ return QStringList(fontLocation);
+
+ const QByteArray ba = qgetenv("QT_OpenHarmony_FONT_LOCATION");
+ if (!ba.isEmpty())
+ return QStringList((fontLocation = QDir::cleanPath(QString::fromLocal8Bit(ba))));
+
+ // Don't cache the fallback, as we might just have been called before
+ // QT_OpenHarmony_FONT_LOCATION has been set.
+ return QStringList(QLatin1String("/system/fonts"));
+ }
+
+ return QStringList(writableLocation(type));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_STANDARDPATHS
@@ -22,7 +22,7 @@
#include "private/qfile_p.h"
#include "qtemporaryfile.h"
-#if defined(Q_OS_LINUX) && QT_CONFIG(linkat)
+#if defined(Q_OS_LINUX) && QT_CONFIG(linkat) && !defined(Q_OS_OPENHARMONY)
# include <fcntl.h>
# ifdef O_TMPFILE
// some early libc support had the wrong values for O_TMPFILE
@@ -4,6 +4,10 @@
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
+#ifdef Q_OS_OPENHARMONY
+// QtWatchdog
+#include "qohoswatchdog.h"
+#endif
#ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h"
@@ -1388,6 +1392,11 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m
}
}
+#ifdef Q_OS_OPENHARMONY
+// TODO QtWatchdog
+std::shared_ptr<QtOhosWatchdog::QOhosWatchdog> qohoswatchdog_;
+#endif
+
/*****************************************************************************
Main event loop wrappers
*****************************************************************************/
@@ -1432,6 +1441,13 @@ int QCoreApplication::exec()
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
+#ifdef Q_OS_OPENHARMONY
+ // QtWatchdog
+ qWarning("QCoreApplication: exec start.");
+ qohoswatchdog_ = std::make_shared<QtOhosWatchdog::QOhosWatchdog>();
+ qohoswatchdog_->Init();
+ qWarning("QCoreApplication: exec end.");
+#endif
threadData->quitNow = false;
QEventLoop eventLoop;
@@ -2024,9 +2040,29 @@ void QCoreApplicationPrivate::removePostedEvent(QEvent * event)
bool QCoreApplication::event(QEvent *e)
{
if (e->type() == QEvent::Quit) {
+#ifdef Q_OS_OPENHARMONY
+ // QtWatchdog
+ if (qohoswatchdog_ != nullptr) {
+ qohoswatchdog_->Stop();
+ qohoswatchdog_ = nullptr;
+ }
+#endif
exit(0);
return true;
}
+#ifdef Q_OS_OPENHARMONY
+ // QtWatchdog
+ if (e->type() == QEvent::CheckMainThreadIsAlive) {
+ qWarning("QCoreApplication: receive check main thread event start.");
+ if (qohoswatchdog_ == nullptr) {
+ qWarning("QCoreApplication: Qt Watch dog is nullptr.");
+ } else {
+ qohoswatchdog_->SetAppMainThreadState(true);
+ qohoswatchdog_->AllowReportEvent();
+ }
+ return true;
+ }
+#endif
return QObject::event(e);
}
@@ -287,6 +287,9 @@ public:
// 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event
+ // QtWatchdog
+ CheckMainThreadIsAlive = 601, // oh watch dog
+
User = 1000, // first user event id
MaxUser = 65535 // last user event id
};
new file mode 100644
@@ -0,0 +1,45 @@
+#include <QHash>
+#include <QByteArray>
+#include <QReadWriteLock>
+
+#include "qjsmodule.h"
+#include "qopenharmonydefines.h"
+
+QT_BEGIN_NAMESPACE
+inline static QReadWriteLock g_locker;
+inline static QHash<QString, Napi::Object> g_modules{};
+QJsModule::QJsModule(const QString &module)
+{
+ auto creator = [this, module]{
+ {
+ QReadLocker locker(&g_locker);
+ if (g_modules.contains(module)) {
+ setObject(g_modules.value(module));
+ return;
+ }
+ }
+
+ napi_value result;
+ const QByteArray data = module.toLatin1();
+ QString path = QtOh::bundleName() + "/" + QtOh::moduleName();
+ QByteArray pathByteArray = path.toUtf8();
+ Napi::Env env = QtOh::uiEnv();
+
+ napi_status status = napi_load_module_with_info(env,
+ data.constData(), pathByteArray.constData(), &result);
+ if (status == napi_ok && result != nullptr) {
+ Napi::Object obj(env, result);
+ setObject(obj);
+ {
+ QWriteLocker locke(&g_locker);
+ g_modules.insert(module, obj);
+ }
+ }
+ LOGI("load js module: [%{public}s], napi_status: [%{public}d], result: [%{public}p, [%{public}s]]",
+ module.toLocal8Bit().data(), status, result, path.toLocal8Bit().data());
+ };
+
+ QtOh::runOnJsUIThreadAndWait(creator);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,16 @@
+#ifndef QJSMODULE_H
+#define QJSMODULE_H
+
+#include <QString>
+#include <qjsobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QJsModule : public QJsObject
+{
+public:
+ QJsModule(const QString &module);
+};
+
+QT_END_NAMESPACE
+#endif // QJSMODULE_H
new file mode 100644
@@ -0,0 +1,169 @@
+#include <QDebug>
+#include <QMapIterator>
+
+#include "qjsobject.h"
+#include "qjsobject_p.h"
+#include "qopenharmony_p.h"
+#include "qopenharmonydefines.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsObjectPrivate::QJsObjectPrivate(QJsObject * const qq)
+ : q_ptr(qq)
+{
+}
+
+QJsObjectPrivate::QJsObjectPrivate(QJsObject * const qq, Napi::Object jsObject)
+ : QJsObjectPrivate(qq)
+{
+ setObjectImpl(jsObject);
+}
+
+QJsObjectPrivate::QJsObjectPrivate(QJsObject * const qq, Napi::Value constructor, const std::initializer_list<napi_value>& args)
+ : QJsObjectPrivate(qq)
+{
+ setConstructor(constructor, args);
+}
+
+QJsObjectPrivate::~QJsObjectPrivate()
+{
+
+}
+
+bool QJsObjectPrivate::isCallFromUiThread() const
+{
+ return QtOh::uiThreadId() == std::this_thread::get_id();
+}
+
+void QJsObjectPrivate::setObjectImpl(Napi::Object jsObject)
+{
+ m_ref = Napi::Persistent(jsObject);
+}
+
+void QJsObjectPrivate::setConstructor(Napi::Value constructor, const std::initializer_list<napi_value>& args)
+{
+ napi_value instance;
+ napi_env env = constructor.Env();
+ napi_status status = napi_new_instance(env, constructor, args.size(), args.begin(), &instance);
+ if (status == napi_ok) {
+ setObjectImpl(Napi::Object(env, instance));
+ }
+}
+
+QJsObject::QJsObject(Napi::Value constructor, const std::initializer_list<napi_value> &args)
+ : QJsObject(*(new QJsObjectPrivate(this, constructor, args)))
+{
+}
+
+QJsObject::QJsObject(Napi::Object jsObject)
+ : QJsObject(*(new QJsObjectPrivate(this, jsObject)))
+{
+}
+
+QJsObject::~QJsObject()
+{
+ Q_D(QJsObject);
+ QtOh::runOnJsUIThreadAndWait([d]{
+ d->m_ref.SuppressDestruct();
+ d->m_ref.Reset();
+ });
+}
+
+Napi::Env QJsObject::env() const
+{
+ Q_D(const QJsObject);
+ return d->m_ref.Env();
+}
+
+Napi::Object QJsObject::object() const
+{
+ Q_D(const QJsObject);
+ return d->m_ref.Value();
+}
+
+QJsObject::operator Napi::Object() const
+{
+ return object();
+}
+
+void QJsObject::setObject(Napi::Object object)
+{
+ Q_D(QJsObject);
+ d->setObjectImpl(object);
+}
+
+Napi::Value QJsObject::get(const QString &key) const
+{
+ clearError();
+ return object().Get(key.toStdString());
+}
+
+bool QJsObject::hasError() const
+{
+ return env().IsExceptionPending();
+}
+
+void QJsObject::clearError() const
+{
+ Q_D(const QJsObject);
+ if (hasError()) {
+ d->m_errorString.clear();
+ Napi::Error e = env().GetAndClearPendingException();
+ if (!e.IsEmpty())
+ d->m_errorString = QString::fromStdString(e.Message());
+ }
+}
+
+QString QJsObject::lastError() const
+{
+ Q_D(const QJsObject);
+ return d->m_errorString;
+}
+
+bool QJsObject::shouldExecInUIJsThread() const
+{
+ Q_D(const QJsObject);
+ return !d->isCallFromUiThread();
+}
+
+Napi::Value QJsObject::callImpl(const QString &function, const std::initializer_list<napi_value>& args) const
+{
+ clearError();
+ Napi::Value functionValue = object().Get(function.toStdString());
+ if (!functionValue.IsFunction()) {
+ LOGW("Method '%{public}s' not found or not a function", qPrintable(function));
+ if (hasError()) {
+ clearError();
+ LOGE("js func error:%{public}s", qPrintable(lastError()));
+ }
+ return env().Null();
+ }
+
+ Napi::Function func = functionValue.As<Napi::Function>();
+ if (func.IsNull())
+ return env().Null();
+
+ Napi::Value result = func.Call(object(), args);
+ if (hasError()) {
+ clearError();
+ LOGE("call %{public}s has error: %{public}s", qPrintable(function), qPrintable(lastError()));
+ }
+ return result;
+}
+
+QJsObject::QJsObject(QJsObjectPrivate &dd, Napi::Value constructor)
+ : d_ptr(&dd)
+{
+}
+
+QJsObject::QJsObject(QJsObjectPrivate &dd, Napi::Object jsObject)
+ : d_ptr(&dd)
+{
+}
+
+QJsSubEvent::QJsSubEvent(const QString &type, QJsObject::QNapiCallBack callback, napi_env env)
+ : type(type.toStdString()), callback(Napi::Function::New(env, callback))
+{
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,91 @@
+#ifndef QJSOBJECT_H
+#define QJSOBJECT_H
+
+#include <QString>
+#include <functional>
+#include <QVariantMap>
+#include <qopenharmony.h>
+#include <qnapi.h>
+
+QT_BEGIN_NAMESPACE
+
+class QJsObjectPrivate;
+class Q_CORE_EXPORT QJsObject
+{
+ Q_DECLARE_PRIVATE(QJsObject)
+ Q_DISABLE_COPY_MOVE(QJsObject)
+
+public:
+ using QNapiCallBack = std::function<void(const Napi::CallbackInfo &)>;
+ explicit QJsObject(Napi::Value constructor, const std::initializer_list<napi_value>& args = std::initializer_list<napi_value>());
+ explicit QJsObject(Napi::Object jsObject = Napi::Object());
+ virtual ~QJsObject();
+
+ Napi::Env env() const;
+ Napi::Object object() const;
+ operator Napi::Object() const;
+ void setObject(Napi::Object object);
+
+ Napi::Value get(const QString &key) const;
+ template <typename RET>
+ typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+ get(const QString &key) const;
+
+ bool hasError() const;
+ void clearError() const;
+ QString lastError() const;
+ bool shouldExecInUIJsThread() const;
+
+ template <typename ValueType>
+ void set(const QString &key, const ValueType &value);
+
+ template<typename RET = Napi::Value, typename... Args>
+ inline typename std::enable_if<(sizeof...(Args) > 0), RET>::type
+ call(const QString &function, Args&&... args) const {
+ auto result = callImpl(function, { QNapi::create(std::forward<Args>(args))... });
+ return QNapi::get<RET>(result);
+ }
+ template<typename RET = Napi::Value>
+ inline RET call(const QString &function, const std::initializer_list<napi_value>& args = {}) const {
+ auto result = callImpl(function, args);
+ return QNapi::get<RET>(result);
+ }
+protected:
+ Napi::Value callImpl(const QString &function, const std::initializer_list<napi_value>& args) const;
+ QScopedPointer<QJsObjectPrivate> d_ptr;
+ explicit QJsObject(QJsObjectPrivate &dd, Napi::Value constructor);
+ explicit QJsObject(QJsObjectPrivate &dd, Napi::Object jsObject = Napi::Object());
+};
+
+template<typename RET>
+typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+QJsObject::get(const QString &key) const
+{
+ RET result{};
+ QtOh::runOnJsUIThreadAndWait([this, key, &result]{
+ clearError();
+ result = QNapi::get<RET>(object(), key);
+ });
+ return result;
+}
+
+template<typename ValueType>
+inline void QJsObject::set(const QString &key, const ValueType &value)
+{
+ QtOh::runOnJsUIThreadAndWait([this, key, &value]{
+ clearError();
+ object().Set(key.toStdString(), QNapi::create(value));
+ });
+}
+
+class Q_CORE_EXPORT QJsSubEvent
+{
+public:
+ QJsSubEvent(const QString &type, QJsObject::QNapiCallBack callback, napi_env env);
+ const std::string type;
+ const Napi::Function callback;
+};
+
+QT_END_NAMESPACE
+#endif // QJSOBJECT_H
+
new file mode 100644
@@ -0,0 +1,35 @@
+#ifndef QJSOBJECT_P_H
+#define QJSOBJECT_P_H
+
+#include <QString>
+#include <qglobal.h>
+#include <qopenharmony.h>
+
+QT_BEGIN_NAMESPACE
+class QJsObject;
+
+class Q_CORE_EXPORT QJsObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QJsObject)
+ Q_DISABLE_COPY_MOVE(QJsObjectPrivate)
+
+public:
+ explicit QJsObjectPrivate(QJsObject *const qq);
+ explicit QJsObjectPrivate(QJsObject *const qq, Napi::Object jsObject);
+ explicit QJsObjectPrivate(QJsObject *const qq, Napi::Value constructor, const std::initializer_list<napi_value> &args);
+ virtual ~QJsObjectPrivate();
+
+protected:
+ QJsObject *const q_ptr;
+ Napi::ObjectReference m_ref;
+ mutable QString m_errorString;
+
+private:
+ bool isCallFromUiThread() const;
+ void setObjectImpl(Napi::Object jsObject);
+ void setConstructor(Napi::Value constructor, const std::initializer_list<napi_value> &args);
+};
+
+QT_END_NAMESPACE
+
+#endif //QJSOBJECT_P_H
new file mode 100644
@@ -0,0 +1,53 @@
+#include "qjspromise_p.h"
+#include "qjsobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsPromisePrivate : public QJsObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QJsPromise)
+public:
+ QJsPromisePrivate(QJsObject *const qq, const Napi::Promise &promise);
+ Napi::Function createCallback(std::function<void(const Napi::CallbackInfo&)>& callback,
+ const char* name);
+};
+
+QJsPromise::QJsPromise(const Napi::Promise &promise)
+ : QJsObject(*new QJsPromisePrivate(this, promise))
+{
+}
+
+QJsPromise &QJsPromise::onThen(std::function<void(const Napi::CallbackInfo&)> callback)
+{
+ Q_D(QJsPromise);
+ call("then", {d->createCallback(callback, "resolvedResult")});
+ return *this;
+}
+
+QJsPromise& QJsPromise::onCatch(std::function<void(const Napi::CallbackInfo&)> callback)
+{
+ Q_D(QJsPromise);
+ call("catch", {d->createCallback(callback, "catchResult")});
+ return *this;
+}
+
+QJsPromisePrivate::QJsPromisePrivate(QJsObject *const qq, const Napi::Promise &promise)
+ : QJsObjectPrivate(qq, promise)
+{
+
+}
+
+Napi::Function QJsPromisePrivate::createCallback(std::function<void (const Napi::CallbackInfo &)> &callback, const char *name) {
+ Q_Q(QJsPromise);
+ return Napi::Function::New(
+ q->env(),
+ [cb = std::move(callback)](const Napi::CallbackInfo& info) {
+ if (cb) {
+ cb(info);
+ }
+ }, name);
+}
+
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QJSPROMISE_P_H
+#define QJSPROMISE_P_H
+
+#include <QJsObject>
+
+QT_BEGIN_NAMESPACE
+class QJsPromisePrivate;
+
+class Q_CORE_EXPORT QJsPromise : public QJsObject
+{
+ Q_DECLARE_PRIVATE(QJsPromise)
+public:
+ QJsPromise(const Napi::Promise& promise);
+
+ QJsPromise &onThen(std::function<void(const Napi::CallbackInfo&)> callback);
+
+ QJsPromise& onCatch(std::function<void(const Napi::CallbackInfo&)> callback);
+};
+
+QT_END_NAMESPACE
+
+#endif //QJSPROMISE_P_H
new file mode 100644
@@ -0,0 +1,841 @@
+#ifndef QNAPI_H
+#define QNAPI_H
+#include <qopenharmony.h>
+#include <QtCore/QString>
+#include <QtCore/QVariant>
+#include <QtCore/QVariantMap>
+#include <QtCore/QStringList>
+#include <QtCore/QVariantList>
+
+QT_BEGIN_NAMESPACE
+
+namespace QNapi {
+
+inline Napi::Value variant2Value(const QVariant& variant);
+
+inline Napi::Object createObject()
+{
+ return Napi::Object::New(QtOh::uiEnv());
+}
+
+//#if __cplusplus >= 201703L
+//template<typename T>
+//inline std::enable_if_t<std::is_enum_v<std::decay_t<T>>, Napi::Value>
+//create(T value)
+//{
+// return Napi::Value::From(QtOh::uiEnv(), static_cast<std::underlying_type_t<T>>(value));
+//}
+
+//template<typename T>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<T>, QString>, Napi::Value>
+//create(T&& value)
+//{
+// return Napi::String::New(QtOh::uiEnv(), value.toStdString());
+//}
+
+//template<typename T>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<T>, QStringList>, Napi::Value>
+//create(T&& value)
+//{
+// auto env = QtOh::uiEnv();
+// auto jsArray = Napi::Array::New(env, value.size());
+
+// for (int i = 0; i < value.size(); ++i) {
+// jsArray.Set(i, create(value[i]));
+// }
+
+// return jsArray;
+//}
+
+//template<typename T>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<T>, QVariantList>, Napi::Value>
+//create(T&& value)
+//{
+// auto env = QtOh::uiEnv();
+// auto jsArray = Napi::Array::New(env, value.size());
+
+// for (int i = 0; i < value.size(); ++i) {
+// jsArray.Set(i, variant2Value(value[i]));
+// }
+
+// return jsArray;
+//}
+
+//template<typename T>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<T>, Napi::Function>, Napi::Function>
+//create(T&& value)
+//{
+// return std::forward<T>(value);
+//}
+
+//template<typename Callable>
+//inline std::enable_if_t<std::is_invocable_v<Callable, const Napi::CallbackInfo&>, Napi::Function>
+//create(Callable&& cb, const char* utf8name = nullptr, void* data = nullptr)
+//{
+// return Napi::Function::New(QtOh::uiEnv(), std::forward<Callable>(cb), utf8name, data);
+//}
+
+//template<typename Callable>
+//inline std::enable_if_t<std::is_invocable_v<Callable, const Napi::CallbackInfo&>, Napi::Function>
+//create(Callable&& cb, const QString& utf8name, void* data = nullptr)
+//{
+// return Napi::Function::New(QtOh::uiEnv(), std::forward<Callable>(cb), utf8name.toUtf8().constData(), data);
+//}
+
+//template<typename T>
+//inline std::enable_if_t<!std::is_enum_v<std::decay_t<T>> &&
+// !std::is_same_v<std::decay_t<T>, QString> &&
+// !std::is_same_v<std::decay_t<T>, QStringList> &&
+// !std::is_same_v<std::decay_t<T>, QVariantMap> &&
+// !std::is_same_v<std::decay_t<T>, QVariantList> &&
+// !std::is_same_v<std::decay_t<T>, Napi::Function> &&
+// !std::is_invocable_v<T, const Napi::CallbackInfo&>, Napi::Value>
+//create(T&& value) {
+// return Napi::Value::From(QtOh::uiEnv(), std::forward<T>(value));
+//}
+
+//template<typename T>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<T>, QVariantMap>, Napi::Value>
+//create(T&& value) {
+// auto env = QtOh::uiEnv();
+// auto obj = Napi::Object::New(env);
+
+// for (auto it = value.constBegin(); it != value.constEnd(); ++it) {
+// std::string key = it.key().toStdString();
+// obj.Set(key, variant2Value(it.value()));
+// }
+// return obj;
+//}
+
+//inline Napi::Value variant2Value(const QVariant& variant) {
+// if (variant.isNull() || !variant.isValid()) {
+// return Napi::Env(QtOh::uiEnv()).Null();
+// }
+
+// switch (variant.type()) {
+// case QVariant::String:
+// return create(variant.toString());
+// case QVariant::Int:
+// return create(variant.toInt());
+// case QVariant::Double:
+// return create(variant.toDouble());
+// case QVariant::Bool:
+// return create(variant.toBool());
+// case QVariant::Map:
+// return create(variant.toMap());
+// case QVariant::List:
+// return create(variant.toList());
+// case QVariant::StringList:
+// return create(variant.toStringList());
+// default:
+// if (variant.canConvert<int>()) {
+// return create(variant.toInt());
+// }
+// return create(variant.toString());
+// }
+//}
+
+//inline QVariant value2Variant(const Napi::Value& value);
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_integral_v<std::decay_t<RET>> &&
+// !std::is_same_v<std::decay_t<RET>, bool>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsNumber()) {
+// return RET{};
+// }
+// return static_cast<RET>(value.As<Napi::Number>().Int32Value());
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_enum_v<std::decay_t<RET>>, RET>
+//get(const Napi::Value& value)
+//{
+// using DecayRET = std::decay_t<RET>;
+// using UnderlyingType = std::underlying_type_t<DecayRET>;
+// UnderlyingType underlying = get<UnderlyingType>(value);
+// return static_cast<DecayRET>(underlying);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QString>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsString()) {
+// return QString();
+// }
+// std::string str = value.As<Napi::String>();
+// return QString::fromStdString(str);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QByteArray>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsString()) {
+// return QByteArray();
+// }
+// std::string str = value.As<Napi::String>();
+// return QByteArray::fromStdString(str);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QVariantMap>, RET>
+//get(const Napi::Value& value)
+//{
+// QVariantMap result;
+// if (!value.IsObject()) {
+// return result;
+// }
+
+// Napi::Object obj = value.As<Napi::Object>();
+// auto propertyNames = obj.GetPropertyNames();
+// uint32_t length = propertyNames.Length();
+
+// for (uint32_t i = 0; i < length; ++i) {
+// Napi::Value key = propertyNames.Get(i);
+// Napi::Value val = obj.Get(key);
+
+// if (key.IsString()) {
+// QString qKey = QString::fromStdString(key.As<Napi::String>());
+// QVariant qValue = value2Variant(val);
+// result.insert(qKey, qValue);
+// }
+// }
+// return result;
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QVariantList>, RET>
+//get(const Napi::Value& value)
+//{
+// QVariantList result;
+// if (!value.IsArray()) {
+// return result;
+// }
+
+// Napi::Array array = value.As<Napi::Array>();
+// uint32_t length = array.Length();
+
+// for (uint32_t i = 0; i < length; ++i) {
+// Napi::Value element = array.Get(i);
+// result.append(value2Variant(element));
+// }
+// return result;
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QStringList>, RET>
+//get(const Napi::Value& value)
+//{
+// QStringList result;
+// if (!value.IsArray()) {
+// return result;
+// }
+
+// Napi::Array array = value.As<Napi::Array>();
+// uint32_t length = array.Length();
+
+// for (uint32_t i = 0; i < length; ++i) {
+// Napi::Value element = array.Get(i);
+// result << get<QString>(element);
+// }
+// return result;
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, QVariant>, RET>
+//get(const Napi::Value& value)
+//{
+// return value2Variant(value);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, bool>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsBoolean()) {
+// return false;
+// }
+// return value.As<Napi::Boolean>();
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_floating_point_v<std::decay_t<RET>>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsNumber()) {
+// return RET{};
+// }
+// return static_cast<RET>(value.As<Napi::Number>().DoubleValue());
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_same_v<std::decay_t<RET>, std::string>, RET>
+//get(const Napi::Value& value)
+//{
+// if (!value.IsString()) {
+// return std::string{};
+// }
+// return value.As<Napi::String>();
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<!std::is_enum_v<std::decay_t<RET>> &&
+// !std::is_same_v<std::decay_t<RET>, QString> &&
+// !std::is_same_v<std::decay_t<RET>, QByteArray> &&
+// !std::is_same_v<std::decay_t<RET>, QVariantMap> &&
+// !std::is_same_v<std::decay_t<RET>, QVariantList> &&
+// !std::is_same_v<std::decay_t<RET>, QStringList> &&
+// !std::is_same_v<std::decay_t<RET>, QVariant> &&
+// !std::is_same_v<std::decay_t<RET>, bool> &&
+// !std::is_integral_v<std::decay_t<RET>> &&
+// !std::is_floating_point_v<std::decay_t<RET>> &&
+// !std::is_same_v<std::decay_t<RET>, std::string>, RET>
+//get(const Napi::Value& value)
+//{
+// return value.As<RET>();
+//}
+
+//inline QVariant value2Variant(const Napi::Value& value)
+//{
+// if (value.IsNull() || value.IsUndefined()) {
+// return QVariant();
+// } else if (value.IsBoolean()) {
+// return QVariant(get<bool>(value));
+// } else if (value.IsNumber()) {
+// double num = value.As<Napi::Number>().DoubleValue();
+// if (std::floor(num) == num && num >= std::numeric_limits<int>::min() && num <= std::numeric_limits<int>::max()) {
+// return QVariant(get<int>(value));
+// } else {
+// return QVariant(get<double>(value));
+// }
+// } else if (value.IsString()) {
+// return QVariant(get<QString>(value));
+// } else if (value.IsArray()) {
+// return QVariant(get<QVariantList>(value));
+// } else if (value.IsObject()) {
+// return QVariant(get<QVariantMap>(value));
+// }
+// return QVariant(get<QString>(value.ToString()));
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_default_constructible_v<RET>, RET>
+//get(const Napi::Object& object, const char *property)
+//{
+// if (!object.Has(property)) {
+// return RET{};
+// }
+// Napi::Value value = object.Get(property);
+// return get<RET>(value);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_default_constructible_v<RET>, RET>
+//get(const Napi::Object& object, const QString& property)
+//{
+// return get<RET>(object, property.toUtf8().constData());
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_default_constructible_v<RET>, RET>
+//get(const Napi::Value& value, const char *property)
+//{
+// if (!value.IsObject()) {
+// return RET{};
+// }
+
+// Napi::Object object = value.As<Napi::Object>();
+// return get<RET>(object, property);
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_default_constructible_v<RET>, RET>
+//get(const Napi::Value& value, const QString& property)
+//{
+// return get<RET>(value, property.toUtf8().constData());
+//}
+
+//template<typename RET>
+//inline RET getOrDefault(const Napi::Object& object, const char *property, const RET& defaultValue)
+//{
+// if (!object.Has(property)) {
+// return defaultValue;
+// }
+
+// Napi::Value propertyValue = object.Get(property);
+// return get<RET>(propertyValue);
+//}
+
+//template<typename RET>
+//inline RET getOrDefault(const Napi::Value& value, const char *property, const RET& defaultValue)
+//{
+// if (!value.IsObject()) {
+// return defaultValue;
+// }
+
+// Napi::Object object = value.As<Napi::Object>();
+// if (!object.Has(property)) {
+// return defaultValue;
+// }
+
+// Napi::Value propertyValue = object.Get(property);
+// return get<RET>(propertyValue);
+//}
+
+//template<typename T>
+//inline void set(Napi::Object& object, const char *property, T&& value)
+//{
+// object.Set(property, create(std::forward<T>(value)));
+//}
+
+//template<typename T>
+//inline void set(Napi::Object& object, const QString &property, T&& value)
+//{
+// object.Set(property.toStdString(), create(std::forward<T>(value)));
+//}
+
+//template<typename RET>
+//inline std::enable_if_t<std::is_default_constructible_v<RET>, RET>
+//getFirst(const Napi::CallbackInfo &info)
+//{
+// if (info.Length() < 1)
+// return RET{};
+// return get<RET>(info[0]);
+//}
+//#else
+template<typename T>
+inline typename std::enable_if<std::is_enum<typename std::decay<T>::type>::value, Napi::Value>::type
+create(T value)
+{
+ return Napi::Value::From(QtOh::uiEnv(), static_cast<typename std::underlying_type<typename std::decay<T>::type>::type>(value));
+}
+
+template<typename T>
+inline typename std::enable_if<std::is_same<typename std::decay<T>::type, QString>::value, Napi::Value>::type
+create(T&& value)
+{
+ return Napi::String::New(QtOh::uiEnv(), value.toStdString());
+}
+
+template<typename T>
+inline typename std::enable_if<std::is_same<typename std::decay<T>::type, QStringList>::value, Napi::Value>::type
+create(T&& value)
+{
+ auto env = QtOh::uiEnv();
+ auto jsArray = Napi::Array::New(env, value.size());
+
+ for (int i = 0; i < value.size(); ++i) {
+ jsArray.Set(i, create(value[i]));
+ }
+
+ return jsArray;
+}
+
+template<typename T>
+inline typename std::enable_if<std::is_same<typename std::decay<T>::type, QVariantList>::value, Napi::Value>::type
+create(T&& value)
+{
+ auto env = QtOh::uiEnv();
+ auto jsArray = Napi::Array::New(env, value.size());
+
+ for (int i = 0; i < value.size(); ++i) {
+ jsArray.Set(i, variant2Value(value[i]));
+ }
+
+ return jsArray;
+}
+
+template<typename T>
+inline typename std::enable_if<std::is_same<typename std::decay<T>::type, Napi::Function>::value, Napi::Function>::type
+create(T&& value)
+{
+ return std::forward<T>(value);
+}
+
+template<typename T, typename = void>
+struct is_invocable : std::false_type {};
+
+template<typename T>
+struct is_invocable<T, typename std::enable_if<
+ std::is_convertible<
+ decltype(std::declval<T>()(std::declval<const Napi::CallbackInfo&>())),
+ Napi::Value
+ >::value ||
+ std::is_same<
+ decltype(std::declval<T>()(std::declval<const Napi::CallbackInfo&>())),
+ void
+ >::value
+ >::type> : std::true_type {};
+
+template<>
+struct is_invocable<Napi::Value(*)(const Napi::CallbackInfo&)> : std::true_type {};
+
+template<>
+struct is_invocable<void(*)(const Napi::CallbackInfo&)> : std::true_type {};
+
+template<typename Callable>
+inline typename std::enable_if<
+ is_invocable<typename std::decay<Callable>::type>::value &&
+ !std::is_same<typename std::decay<Callable>::type, Napi::Function>::value,
+ Napi::Function
+ >::type
+create(Callable&& cb)
+{
+ return Napi::Function::New(QtOh::uiEnv(), std::forward<Callable>(cb));
+}
+
+template<typename Callable>
+inline typename std::enable_if<
+ is_invocable<typename std::decay<Callable>::type>::value &&
+ !std::is_same<typename std::decay<Callable>::type, Napi::Function>::value,
+ Napi::Function
+ >::type
+create(Callable&& cb, const char* utf8name, void* data)
+{
+ return Napi::Function::New(QtOh::uiEnv(), std::forward<Callable>(cb), utf8name, data);
+}
+
+template<typename Callable>
+inline typename std::enable_if<
+ is_invocable<typename std::decay<Callable>::type>::value &&
+ !std::is_same<typename std::decay<Callable>::type, Napi::Function>::value,
+ Napi::Function
+ >::type
+create(Callable&& cb, const QString& utf8name, void* data)
+{
+ return Napi::Function::New(QtOh::uiEnv(), std::forward<Callable>(cb), utf8name.toUtf8().constData(), data);
+}
+
+template<typename T>
+inline typename std::enable_if<!std::is_enum<typename std::decay<T>::type>::value &&
+ !std::is_same<typename std::decay<T>::type, QString>::value &&
+ !std::is_same<typename std::decay<T>::type, QStringList>::value &&
+ !std::is_same<typename std::decay<T>::type, QVariantMap>::value &&
+ !std::is_same<typename std::decay<T>::type, QVariantList>::value &&
+ !std::is_same<typename std::decay<T>::type, Napi::Function>::value &&
+ !is_invocable<T>::value, Napi::Value>::type
+create(T&& value) {
+ return Napi::Value::From(QtOh::uiEnv(), std::forward<T>(value));
+}
+
+template<typename T>
+inline typename std::enable_if<std::is_same<typename std::decay<T>::type, QVariantMap>::value, Napi::Value>::type
+create(T&& value) {
+ auto env = QtOh::uiEnv();
+ auto obj = Napi::Object::New(env);
+
+ for (auto it = value.constBegin(); it != value.constEnd(); ++it) {
+ std::string key = it.key().toStdString();
+ obj.Set(key, variant2Value(it.value()));
+ }
+ return obj;
+}
+
+inline Napi::Value variant2Value(const QVariant& variant) {
+ if (variant.isNull() || !variant.isValid()) {
+ return Napi::Env(QtOh::uiEnv()).Null();
+ }
+
+ switch (variant.type()) {
+ case QVariant::String:
+ return create(variant.toString());
+ case QVariant::Int:
+ return create(variant.toInt());
+ case QVariant::Double:
+ return create(variant.toDouble());
+ case QVariant::Bool:
+ return create(variant.toBool());
+ case QVariant::Map:
+ return create(variant.toMap());
+ case QVariant::List:
+ return create(variant.toList());
+ case QVariant::StringList:
+ return create(variant.toStringList());
+ default:
+ if (variant.canConvert<int>()) {
+ return create(variant.toInt());
+ }
+ return create(variant.toString());
+ }
+}
+
+inline QVariant value2Variant(const Napi::Value& value);
+
+template<typename RET>
+inline typename std::enable_if<std::is_integral<typename std::decay<RET>::type>::value &&
+ !std::is_same<typename std::decay<RET>::type, bool>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsNumber()) {
+ return RET{};
+ }
+ return static_cast<RET>(value.As<Napi::Number>().Int32Value());
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_enum<typename std::decay<RET>::type>::value, RET>::type
+get(const Napi::Value& value)
+{
+ using DecayRET = typename std::decay<RET>::type;
+ using UnderlyingType = typename std::underlying_type<DecayRET>::type;
+ UnderlyingType underlying = get<UnderlyingType>(value);
+ return static_cast<DecayRET>(underlying);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QString>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsString()) {
+ return QString();
+ }
+ std::string str = value.As<Napi::String>();
+ return QString::fromStdString(str);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QByteArray>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsString()) {
+ return QByteArray();
+ }
+ std::string str = value.As<Napi::String>();
+ return QByteArray::fromStdString(str);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QVariantMap>::value, RET>::type
+get(const Napi::Value& value)
+{
+ QVariantMap result;
+ if (!value.IsObject()) {
+ return result;
+ }
+
+ Napi::Object obj = value.As<Napi::Object>();
+ auto propertyNames = obj.GetPropertyNames();
+ uint32_t length = propertyNames.Length();
+
+ for (uint32_t i = 0; i < length; ++i) {
+ Napi::Value key = propertyNames.Get(i);
+ Napi::Value val = obj.Get(key);
+
+ if (key.IsString()) {
+ QString qKey = QString::fromStdString(key.As<Napi::String>());
+ QVariant qValue = value2Variant(val);
+ result.insert(qKey, qValue);
+ }
+ }
+ return result;
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QVariantList>::value, RET>::type
+get(const Napi::Value& value)
+{
+ QVariantList result;
+ if (!value.IsArray()) {
+ return result;
+ }
+
+ Napi::Array array = value.As<Napi::Array>();
+ uint32_t length = array.Length();
+
+ for (uint32_t i = 0; i < length; ++i) {
+ Napi::Value element = array.Get(i);
+ result.append(value2Variant(element));
+ }
+ return result;
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QStringList>::value, RET>::type
+get(const Napi::Value& value)
+{
+ QStringList result;
+ if (!value.IsArray()) {
+ return result;
+ }
+
+ Napi::Array array = value.As<Napi::Array>();
+ uint32_t length = array.Length();
+
+ for (uint32_t i = 0; i < length; ++i) {
+ Napi::Value element = array.Get(i);
+ result << get<QString>(element);
+ }
+ return result;
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, QVariant>::value, RET>::type
+get(const Napi::Value& value)
+{
+ return value2Variant(value);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, bool>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsBoolean()) {
+ return false;
+ }
+ return value.ToBoolean();
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_floating_point<typename std::decay<RET>::type>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsNumber()) {
+ return RET{};
+ }
+ return static_cast<RET>(value.As<Napi::Number>().DoubleValue());
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_same<typename std::decay<RET>::type, std::string>::value, RET>::type
+get(const Napi::Value& value)
+{
+ if (!value.IsString()) {
+ return std::string{};
+ }
+ return value.As<Napi::String>();
+}
+
+template<typename RET>
+inline typename std::enable_if<!std::is_enum<typename std::decay<RET>::type>::value &&
+ !std::is_same<typename std::decay<RET>::type, QString>::value &&
+ !std::is_same<typename std::decay<RET>::type, QByteArray>::value &&
+ !std::is_same<typename std::decay<RET>::type, QVariantMap>::value &&
+ !std::is_same<typename std::decay<RET>::type, QVariantList>::value &&
+ !std::is_same<typename std::decay<RET>::type, QStringList>::value &&
+ !std::is_same<typename std::decay<RET>::type, QVariant>::value &&
+ !std::is_same<typename std::decay<RET>::type, bool>::value &&
+ !std::is_integral<typename std::decay<RET>::type>::value &&
+ !std::is_floating_point<typename std::decay<RET>::type>::value &&
+ !std::is_same<typename std::decay<RET>::type, std::string>::value, RET>::type
+get(const Napi::Value& value)
+{
+ return value.As<RET>();
+}
+
+inline QVariant value2Variant(const Napi::Value& value)
+{
+ if (value.IsNull() || value.IsUndefined()) {
+ return QVariant();
+ } else if (value.IsBoolean()) {
+ return QVariant(get<bool>(value));
+ } else if (value.IsNumber()) {
+ double num = value.As<Napi::Number>().DoubleValue();
+ if (std::floor(num) == num && num >= std::numeric_limits<int>::min() && num <= std::numeric_limits<int>::max()) {
+ return QVariant(get<int>(value));
+ } else {
+ return QVariant(get<double>(value));
+ }
+ } else if (value.IsString()) {
+ return QVariant(get<QString>(value));
+ } else if (value.IsArray()) {
+ return QVariant(get<QVariantList>(value));
+ } else if (value.IsObject()) {
+ return QVariant(get<QVariantMap>(value));
+ }
+ return QVariant(get<QString>(value.ToString()));
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+get(const Napi::Object& object, const char *property)
+{
+ if (!object.Has(property)) {
+ return RET{};
+ }
+ Napi::Value value = object.Get(property);
+ return get<RET>(value);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+get(const Napi::Object& object, const QString& property)
+{
+ return get<RET>(object, property.toUtf8().constData());
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+get(const Napi::Value& value, const char *property)
+{
+ if (!value.IsObject()) {
+ return RET{};
+ }
+
+ Napi::Object object = value.As<Napi::Object>();
+ return get<RET>(object, property);
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+get(const Napi::Value& value, const QString& property)
+{
+ return get<RET>(value, property.toUtf8().constData());
+}
+
+template<typename RET>
+inline RET getOrDefault(const Napi::Object& object, const char *property, const RET& defaultValue)
+{
+ if (!object.Has(property)) {
+ return defaultValue;
+ }
+
+ Napi::Value propertyValue = object.Get(property);
+ return get<RET>(propertyValue);
+}
+
+template<typename RET>
+inline RET getOrDefault(const Napi::Value& value, const char *property, const RET& defaultValue)
+{
+ if (!value.IsObject()) {
+ return defaultValue;
+ }
+
+ Napi::Object object = value.As<Napi::Object>();
+ if (!object.Has(property)) {
+ return defaultValue;
+ }
+
+ Napi::Value propertyValue = object.Get(property);
+ return get<RET>(propertyValue);
+}
+
+template<typename T>
+inline void set(Napi::Object& object, const char *property, T&& value)
+{
+ object.Set(property, create(std::forward<T>(value)));
+}
+
+template<typename T>
+inline void set(Napi::Object& object, const QString &property, T&& value)
+{
+ object.Set(property.toStdString(), create(std::forward<T>(value)));
+}
+
+template<typename RET>
+inline typename std::enable_if<std::is_default_constructible<RET>::value, RET>::type
+getFirst(const Napi::CallbackInfo &info)
+{
+ if (info.Length() < 1)
+ return RET{};
+ return get<RET>(info[0]);
+}
+
+//#endif
+}
+
+QT_END_NAMESPACE
+#endif // QNAPI_H
+
new file mode 100644
@@ -0,0 +1,51 @@
+#ifndef QOHHIAPPEVENT_P_H
+#define QOHHIAPPEVENT_P_H
+
+#include <mutex>
+#include <memory>
+#include <string>
+#include <qglobal.h>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtOhosWatchdog
+{
+class Q_CORE_EXPORT QOhHiAppEvent {
+ friend class QOhosWatchdog;
+ Q_DISABLE_COPY_MOVE(QOhHiAppEvent)
+
+ std::atomic<bool> m_dogReported;
+ std::unique_ptr<QJsModule> m_jsHiApp {nullptr};
+
+ inline static std::once_flag sm_once;
+ inline static std::unique_ptr<QOhHiAppEvent> sm_self {nullptr};
+
+ explicit QOhHiAppEvent();
+
+ enum {
+ FAULT = 1, /* 故障类型事件 */
+ STATISTIC = 2, /* 统计类型事件 */
+ SECURITY = 3, /* 安全类型事件 */
+ BEHAVIOR = 4, /* 行为类型事件 */
+ };
+public:
+ ~QOhHiAppEvent() {};
+
+ static QOhHiAppEvent *instance() {
+ std::call_once(sm_once, []{
+ sm_self.reset(new QOhHiAppEvent());
+ });
+ return sm_self.get();
+ }
+
+ void writeDataTag(const std::string &txt);
+
+protected:
+ void writeDataTagImple(const std::string &txt, bool reported = false, bool inner = false);
+};
+}
+
+QT_END_NAMESPACE
+
+#endif
new file mode 100644
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <dlfcn.h>
+#include <unistd.h>
+#include <QDateTime>
+#include <qopenharmony.h>
+#include <QLoggingCategory>
+#include <qopenharmonydefines.h>
+#include <QtCore/qcoreapplication.h>
+
+#include "qohoswatchdog.h"
+#include "qohhiappevent_p.h"
+
+Q_LOGGING_CATEGORY(ohoswatchdog, "qt.ohos.watchdog")
+QT_BEGIN_NAMESPACE
+
+namespace QtOhosWatchdog
+{
+
+QOhHiAppEvent::QOhHiAppEvent()
+{
+ m_dogReported.exchange(false);
+ m_jsHiApp.reset(new QJsModule("@ohos.hiviewdfx.hiAppEvent"));
+}
+
+void QOhHiAppEvent::writeDataTag(const std::string &txt)
+{
+ writeDataTagImple(txt);
+}
+
+void QOhHiAppEvent::writeDataTagImple(const std::string &txt, bool reported, bool inner)
+{
+ QtOh::runOnJsUIThreadAndWait([this, txt, reported, inner] {
+ Napi::HandleScope scope(this->m_jsHiApp->env());
+ Napi::Object appInfo = Napi::Object::New(this->m_jsHiApp->env());
+ Napi::Object params = Napi::Object::New(this->m_jsHiApp->env());
+ params.Set("app", Napi::String::New(this->m_jsHiApp->env(), qAppName().toStdString()));
+ params.Set("timestamp", Napi::Number::New(this->m_jsHiApp->env(), QDateTime::currentMSecsSinceEpoch()));
+
+ appInfo.Set("params", params);
+ appInfo.Set("name", Napi::String::New(this->m_jsHiApp->env(), txt));
+ appInfo.Set("eventType", Napi::Number::New(this->m_jsHiApp->env(), BEHAVIOR));
+ appInfo.Set("domain", Napi::String::New(this->m_jsHiApp->env(), "QOhosWatchdog"));
+ Napi::Function asyncCallback = Napi::Function::New(this->m_jsHiApp->env(), [this, txt, reported, inner](const Napi::CallbackInfo &info) {
+ Napi::Object error = info[0].As<Napi::Object>();
+ if (!error.IsNull()) {
+ Napi::Number code = error.Get("code").ToNumber();
+ Napi::String message = error.Get("message").ToString();
+ LOGW("%{public}s hiAppEvent result code: %{public}d message: %{public}s",
+ txt.c_str(),
+ code.Int32Value(),
+ message.Utf8Value().c_str());
+ } else {
+ LOGW("%{public}s hiAppEvent success to write event.", txt.c_str());
+ }
+
+ if (inner)
+ m_dogReported.exchange(reported);
+ });
+ m_jsHiApp->call("write", {appInfo, asyncCallback});
+ });
+}
+
+namespace
+{
+constexpr uint32_t CHECK_MAIN_THREAD_IS_ALIVE = 1;
+constexpr int RESET_RATIO = 2;
+constexpr int32_t BACKGROUND_REPORT_COUNT_MAX = 5;
+
+#ifdef SUPPORT_ASAN
+constexpr uint32_t CHECK_INTERVAL_TIME = 45000;
+#else
+constexpr uint32_t CHECK_INTERVAL_TIME = 3000;
+#endif
+}
+
+QOhosWatchdog* globalQtWatchdog = nullptr;
+
+QOhosWatchdog::~QOhosWatchdog()
+{
+ globalQtWatchdog = nullptr;
+}
+
+QOhosWatchdog::QOhosWatchdog()
+{
+ globalQtWatchdog = this;
+}
+
+void OH_HiCollie_Task() {
+ globalQtWatchdog->Timer();
+}
+
+void QOhosWatchdog::Init()
+{
+ qCDebug(ohoswatchdog, "QOhosWatchdog:: init.");
+ std::unique_lock<std::mutex> lock(cvMutex_);
+ QCoreApplication::postEvent(QCoreApplication::instance(),
+ new QEvent(QEvent::CheckMainThreadIsAlive), Qt::HighEventPriority);
+ lastWatchTime_ = 0;
+ OH_HiCollie_Init_StuckDetection(OH_HiCollie_Task);
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: init end.");
+}
+
+void QOhosWatchdog::Stop()
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: Stop.");
+ std::unique_lock<std::mutex> lock(cvMutex_);
+ if (stopWatchdog_->load()) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog:: has stoped.");
+ return;
+ }
+ stopWatchdog_->store(true);
+ cvWatchdog_.notify_all();
+}
+
+void QOhosWatchdog::SetAppMainThreadState(const bool appMainThreadState)
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: SetAppMainThreadState. %d", appMainThreadState);
+ std::unique_lock<std::mutex> lock(cvMutex_);
+ appMainThreadIsAlive_->store(appMainThreadState);
+}
+
+void QOhosWatchdog::AllowReportEvent()
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: AllowReportEvent.");
+ std::unique_lock<std::mutex> lock(cvMutex_);
+ needReport_->store(true);
+ isSixSecondEvent_->store(false);
+ backgroundReportCount_->store(0);
+ QOhHiAppEvent::instance()->writeDataTagImple("AllowReportEvent", false, true);
+}
+
+bool QOhosWatchdog::IsReportEvent()
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: IsReportEvent.");
+ if (appMainThreadIsAlive_->load()) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog:: IsReportEvent store false.");
+ appMainThreadIsAlive_->store(false);
+ return false;
+ }
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: AppMainThread is not alive.");
+ return true;
+}
+
+void QOhosWatchdog::Timer()
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: Timer start.");
+ std::unique_lock<std::mutex> lock(cvMutex_, std::defer_lock);
+ if (!lock.owns_lock()) {
+ return;
+ }
+
+ if (stopWatchdog_->load()) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog:: QOhosWatchdog has stoped.");
+ return;
+ }
+ if (!needReport_->load()) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog:: QOhosWatchdog timeout, wait for the handler to recover, and do not send event.");
+ return;
+ }
+
+ if (IsReportEvent()) {
+ ReportEvent();
+ }
+
+ QCoreApplication::postEvent(QCoreApplication::instance(),
+ new QEvent(QEvent::CheckMainThreadIsAlive), Qt::HighEventPriority);
+
+ int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
+ system_clock::now().time_since_epoch()).count();
+ if ((now - lastWatchTime_) >= (CHECK_INTERVAL_TIME / RESET_RATIO)) {
+ lastWatchTime_ = now;
+ }
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: Timer end.");
+}
+
+void QOhosWatchdog::ReportEvent()
+{
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: ReportEvent start.");
+ int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::
+ system_clock::now().time_since_epoch()).count();
+ if ((now - lastWatchTime_) > (RESET_RATIO * CHECK_INTERVAL_TIME) ||
+ (now - lastWatchTime_) < (CHECK_INTERVAL_TIME / RESET_RATIO)) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog: Thread may be blocked, do not report this time. currTime: %llu, lastTime: %llu",
+ static_cast<unsigned long long>(now), static_cast<unsigned long long>(lastWatchTime_));
+ return;
+ }
+
+ if (!needReport_->load()) {
+ qCWarning(ohoswatchdog,"QOhosWatchdog:: ReportEvent return.");
+ return;
+ }
+
+#ifndef APP_NO_RESPONSE_DIALOG
+ if (isSixSecondEvent_->load()) {
+ needReport_->store(false);
+ }
+#endif
+ qCDebug(ohoswatchdog,"WatchdogInner QOhosWatchdog:: ReportEvent. isSixSecondEvent_ = %d", isSixSecondEvent_->load());
+ bool temp = isSixSecondEvent_->load();
+ if (temp)
+ QOhHiAppEvent::instance()->writeDataTagImple("ReportEvent", true, true);
+
+ OH_HiCollie_Report(&temp);
+ isSixSecondEvent_->store(temp);
+ qCDebug(ohoswatchdog,"QOhosWatchdog:: ReportEvent end. isSixSecondEvent_ = %d", isSixSecondEvent_->load());
+}
+
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOHOSWATCHDOG_H
+#define QOHOSWATCHDOG_H
+
+#include <string>
+#include <mutex>
+#include <condition_variable>
+#include <atomic>
+#include <hicollie/hicollie.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtOhosWatchdog
+{
+class QOhosWatchdog
+{
+public:
+ QOhosWatchdog();
+ ~QOhosWatchdog();
+
+ /**
+ *
+ * @brief Init the Watchdog.
+ *
+ * @param mainHandler The handler of main thread.
+ */
+ void Init();
+
+ /**
+ *
+ * @brief Stop the mainthread function of watchdog.
+ *
+ */
+ void Stop();
+
+ /**
+ *
+ * @brief Set the state of main thread.
+ *
+ * @param appMainThreadState The state of main thread.
+ */
+ void SetAppMainThreadState(const bool appMainThreadState);
+
+ /**
+ *
+ * @brief Allow report the main thread timeout event.
+ *
+ */
+ void AllowReportEvent();
+
+ /**
+ *
+ * @brief Check and reset the main thread state.
+ *
+ */
+ bool IsReportEvent();
+ void Timer();
+
+private:
+ void ReportEvent();
+ std::mutex cvMutex_;
+ int64_t lastWatchTime_ = 0;
+ std::condition_variable cvWatchdog_;
+ std::shared_ptr<std::atomic<bool>> appMainThreadIsAlive_ = std::make_shared<std::atomic<bool>>(false);
+ std::shared_ptr<std::atomic<bool>> stopWatchdog_ = std::make_shared<std::atomic<bool>>(false);
+ std::shared_ptr<std::atomic<bool>> needReport_ = std::make_shared<std::atomic<bool>>(true);
+ std::shared_ptr<std::atomic<bool>> isSixSecondEvent_ = std::make_shared<std::atomic<bool>>(false);
+ std::shared_ptr<std::atomic<int64_t>> backgroundReportCount_ = std::make_shared<std::atomic<int64_t>>(0);
+};
+}
+
+QT_END_NAMESPACE
+
+#endif /* QOHOSWATCHDOG_H */
new file mode 100644
@@ -0,0 +1,168 @@
+#include "qohutility.h"
+#include <deviceinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline bool equal(const char *s1, const char *s2)
+{
+ return strcmp(s1, s2) == 0;
+}
+namespace QtOh {
+
+Utility::DeviceType Utility::type()
+{
+ const char *t = OH_GetDeviceType();
+ if (equal(t, "2in1")) {
+ return PC;
+ } else if (equal(t, "phone") || equal(t, "default")) {
+ return Phone;
+ } else if (equal(t, "wearable")) {
+ return Wearable;
+ } else if (equal(t, "liteWearable")) {
+ return LiteWearable;
+ } else if (equal(t, "tablet")) {
+ return Tablet;
+ } else if (equal(t, "tv")) {
+ return Tv;
+ } else if (equal(t, "car")) {
+ return Car;
+ } else if (equal(t, "smartVision")) {
+ return SmartVision;
+ }
+ return Unknown;
+}
+
+#define DEVICE_INFO(func) QString::fromUtf8(func)
+
+QString Utility::manufacture()
+{
+ return DEVICE_INFO(OH_GetManufacture());
+}
+
+QString Utility::brand()
+{
+ return DEVICE_INFO(OH_GetBrand());
+}
+
+QString Utility::marketName()
+{
+ return DEVICE_INFO(OH_GetMarketName());
+}
+
+QString Utility::productSeries()
+{
+ return DEVICE_INFO(OH_GetProductSeries());
+}
+
+QString Utility::productModel()
+{
+ return DEVICE_INFO(OH_GetProductModel());
+}
+
+QString Utility::softwareModel()
+{
+ return DEVICE_INFO(OH_GetSoftwareModel());
+}
+
+QString Utility::hardwareModel()
+{
+ return DEVICE_INFO(OH_GetHardwareModel());
+}
+
+QString Utility::bootloaderVersion()
+{
+ return DEVICE_INFO(OH_GetBootloaderVersion());
+}
+
+QString Utility::abiList()
+{
+ return DEVICE_INFO(OH_GetAbiList());
+}
+
+QString Utility::securityPatchTag()
+{
+ return DEVICE_INFO(OH_GetSecurityPatchTag());
+}
+
+QString Utility::displayVersion()
+{
+ return DEVICE_INFO(OH_GetDisplayVersion());
+}
+
+QString Utility::incrementalVersion()
+{
+ return DEVICE_INFO(OH_GetIncrementalVersion());
+}
+
+QString Utility::osReleaseType()
+{
+ return DEVICE_INFO(OH_GetOsReleaseType());
+}
+
+QString Utility::oSFullName()
+{
+ return DEVICE_INFO(OH_GetOSFullName());
+}
+
+int Utility::sdkApiVersion()
+{
+ return OH_GetSdkApiVersion();
+}
+
+int Utility::firstApiVersion()
+{
+ return OH_GetFirstApiVersion();
+}
+
+QString Utility::versionId()
+{
+ return DEVICE_INFO(OH_GetVersionId());
+}
+
+QString Utility::buildType()
+{
+ return DEVICE_INFO(OH_GetBuildType());
+}
+
+QString Utility::buildUser()
+{
+ return DEVICE_INFO(OH_GetBuildUser());
+}
+
+QString Utility::buildHost()
+{
+ return DEVICE_INFO(OH_GetBuildHost());
+}
+
+QString Utility::buildTime()
+{
+ return DEVICE_INFO(OH_GetBuildTime());
+}
+
+QString Utility::buildRootHash()
+{
+ return DEVICE_INFO(OH_GetBuildRootHash());
+}
+
+QString Utility::distributionOSName()
+{
+ return DEVICE_INFO(OH_GetDistributionOSName());
+}
+
+QString Utility::distributionOSVersion()
+{
+ return DEVICE_INFO(OH_GetDistributionOSVersion());
+}
+
+QString Utility::distributionOSReleaseType()
+{
+ return DEVICE_INFO(OH_GetDistributionOSReleaseType());
+}
+
+int Utility::distributionOSApiVersion()
+{
+ return OH_GetDistributionOSApiVersion();
+}
+
+}
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,59 @@
+#ifndef QOHUTILITY_H
+#define QOHUTILITY_H
+
+#include <qstring.h>
+#include <QtCore/qflags.h>
+
+QT_BEGIN_NAMESPACE
+namespace QtOh {
+
+class Q_CORE_EXPORT Utility
+{
+public:
+ enum DeviceType {
+ Unknown = 0x00,
+ PC = 0x01,
+ Phone = 0x02,
+ Wearable = 0x04,
+ LiteWearable = 0x08,
+ Tablet = 0x10,
+ Tv = 0x20,
+ Car = 0x40,
+ SmartVision = 0x80
+ };
+ Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
+
+ static DeviceType type();
+ static QString manufacture();
+ static QString brand();
+ static QString marketName();
+ static QString productSeries();
+ static QString productModel();
+ static QString softwareModel();
+ static QString hardwareModel();
+ static QString bootloaderVersion();
+ static QString abiList();
+ static QString securityPatchTag();
+ static QString displayVersion();
+ static QString incrementalVersion();
+ static QString osReleaseType();
+ static QString oSFullName();
+ static int sdkApiVersion();
+ static int firstApiVersion();
+ static QString versionId();
+ static QString buildType();
+ static QString buildUser();
+ static QString buildHost();
+ static QString buildTime();
+ static QString buildRootHash();
+ static QString distributionOSName();
+ static QString distributionOSVersion();
+ static QString distributionOSReleaseType();
+ static int distributionOSApiVersion();
+};
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QtOh::Utility::DeviceTypes)
+
+QT_END_NAMESPACE
+#endif // QOHUTILITY_H
new file mode 100644
@@ -0,0 +1,180 @@
+#include <QHash>
+#include <filemanagement/file_uri/oh_file_uri.h>
+
+#include "qjsmodule.h"
+#include "qohutility.h"
+#include "qopenharmony.h"
+#include "qopenharmony_p.h"
+#include "qopenharmonydefines.h"
+
+static QString g_bundleName;
+static QString g_moduleName;
+static QString g_abilityName;
+
+static napi_env g_ui_env = nullptr;
+static std::thread::id g_ui_thread_id;
+
+void QtOh::setUIEnv(napi_env env)
+{
+ g_ui_env = env;
+ g_ui_thread_id = std::this_thread::get_id();
+}
+
+napi_env QtOh::uiEnv()
+{
+ return g_ui_env;
+}
+
+void QtOh::setBundleName(const QString &name)
+{
+ g_bundleName = name;
+}
+
+QString QtOh::bundleName()
+{
+ return g_bundleName;
+}
+
+void QtOh::setModuleName(const QString &name)
+{
+ g_moduleName = name;
+}
+
+QString QtOh::moduleName()
+{
+ return g_moduleName;
+}
+
+void QtOh::setAbilityName(const QString &name)
+{
+ g_abilityName = name;
+}
+
+QString QtOh::abilityName()
+{
+ return g_abilityName;
+}
+
+void QtOh::runOnJsUIThreadNoWait(const std::function<void ()> &f)
+{
+ uv_loop_s *loop = nullptr;
+ napi_get_uv_event_loop(uiEnv(), &loop);
+ if (loop == nullptr) {
+ return;
+ }
+ uv_work_t *work = new uv_work_t;
+ if (work == nullptr) {
+ return;
+ }
+
+ struct TaskContext
+ {
+ std::function<void()> function;
+ };
+
+ TaskContext *p = new TaskContext { f };
+ work->data = p;
+ uv_work_cb uvWork = [](uv_work_t *work) {};
+ uv_after_work_cb uvAfterWork = [](uv_work_t *work, int _status){
+ TaskContext *p = (TaskContext *)(work->data);
+ p->function();
+ delete p;
+ delete work;
+ };
+ uv_queue_work(loop, work, uvWork, uvAfterWork);
+}
+
+void QtOh::runOnJsUIThreadAndWait(const std::function<void ()> &f)
+{
+ if (std::this_thread::get_id() == g_ui_thread_id) {
+ f();
+ return;
+ }
+ uv_loop_s *loop = nullptr;
+ napi_get_uv_event_loop(uiEnv(), &loop);
+ if (loop == nullptr) {
+ return;
+ }
+ uv_work_t *work = new uv_work_t;
+ if (work == nullptr) {
+ return;
+ }
+
+ struct TaskContext
+ {
+ std::function<void()> function;
+ std::promise<void> promise;
+ };
+
+ TaskContext *p = new TaskContext { f };
+ work->data = p;
+ uv_work_cb uvWork = [](uv_work_t *work) {};
+ uv_after_work_cb uvAfterWork = [](uv_work_t *work, int _status){
+ TaskContext *p = (TaskContext *)(work->data);
+ p->function();
+ p->promise.set_value();
+ };
+ uv_queue_work(loop, work, uvWork, uvAfterWork);
+ auto future = p->promise.get_future();
+ future.wait();
+
+ delete p;
+ p = nullptr;
+
+ delete work;
+ work = nullptr;
+}
+
+std::thread::id QtOh::uiThreadId()
+{
+ return g_ui_thread_id;
+}
+
+static QHash<QString, QString> g_dirs;
+
+QString QtOh::dir(const QString &dirId)
+{
+ return g_dirs.value(dirId);
+}
+
+void QtOh::setDir(const QString &dirId, const QString &dir)
+{
+ g_dirs.insert(dirId, dir);
+}
+
+int QtOh::apiVersion()
+{
+ return QtOh::Utility::sdkApiVersion();
+}
+
+struct FreeDeleter {
+ void operator()(char* ptr) const {
+ std::free(ptr);
+ }
+};
+
+QString QtOh::pathFromUri(const QString &uri)
+{
+ char *data = nullptr;
+ const QByteArray utf8 = uri.toUtf8();
+ const char *cpath = utf8.constData();
+ FileManagement_ErrCode errCode = OH_FileUri_GetPathFromUri(cpath, strlen(cpath), &data);
+ if (ERR_OK != errCode || nullptr == data) {
+ return QString();
+ }
+ std::unique_ptr<char, FreeDeleter> guard(data);
+ return QString::fromUtf8(data);
+}
+
+QString QtOh::uriFromPath(const QString &path)
+{
+ char *data = nullptr;
+ const QByteArray utf8 = path.toUtf8();
+ const char *cpath = utf8.constData();
+ FileManagement_ErrCode errCode = OH_FileUri_GetUriFromPath(cpath, strlen(cpath), &data);
+ if (ERR_OK != errCode || nullptr == data) {
+ return QString();
+ }
+ std::unique_ptr<char, FreeDeleter> guard(data);
+ return QString::fromUtf8(data);
+}
new file mode 100644
@@ -0,0 +1,51 @@
+#ifndef QOPENHARMONY_H
+#define QOPENHARMONY_H
+
+#include <uv.h>
+#include <mutex>
+#include <future>
+#include <functional>
+#include <QtCore/qglobal.h>
+#include <napi/native_api.h>
+
+#include <qopenharmonydefines.h>
+/* NOTE must be placed after native_api.h */
+#include <QtNapi/napi.h>
+QT_BEGIN_NAMESPACE
+namespace QtOh {
+
+Q_CORE_EXPORT int apiVersion();
+
+Q_CORE_EXPORT void setBundleName(const QString &name);
+
+Q_CORE_EXPORT void setModuleName(const QString &name);
+
+Q_CORE_EXPORT void setAbilityName(const QString &name);
+
+Q_CORE_EXPORT QString bundleName();
+
+Q_CORE_EXPORT QString moduleName();
+
+Q_CORE_EXPORT QString abilityName();
+
+Q_CORE_EXPORT QString dir(const QString &dirId);
+
+Q_CORE_EXPORT void setDir(const QString &dirId, const QString &dir);
+
+Q_CORE_EXPORT void setUIEnv(napi_env env);
+
+Q_CORE_EXPORT napi_env uiEnv();
+
+Q_CORE_EXPORT std::thread::id uiThreadId();
+
+Q_CORE_EXPORT void runOnJsUIThreadNoWait(const std::function<void()> &f);
+
+Q_CORE_EXPORT void runOnJsUIThreadAndWait(const std::function<void()> &f);
+
+Q_CORE_EXPORT QString pathFromUri(const QString &uri);
+
+Q_CORE_EXPORT QString uriFromPath(const QString &path);
+}
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONY_H
new file mode 100644
@@ -0,0 +1,107 @@
+#ifndef QOPENHARMONY_P_H
+#define QOPENHARMONY_P_H
+
+#include <QtCore/qopenharmony.h>
+
+QT_BEGIN_NAMESPACE
+namespace QtOh {
+
+template <typename Function, typename... Args>
+auto runOnJsUIThreadWithResult(Function&& function, Args&&... args)
+ -> decltype(std::invoke(std::forward<Function>(function), std::forward<Args>(args)...))
+{
+ using ReturnType = decltype(std::invoke(std::forward<Function>(function), std::forward<Args>(args)...));
+
+ if (std::this_thread::get_id() == uiThreadId()) {
+ return std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
+ }
+
+ uv_loop_s *loop = nullptr;
+ napi_get_uv_event_loop(uiEnv(), &loop);
+ if (loop == nullptr) {
+ return ReturnType{};
+ }
+ auto promise = std::make_shared<std::promise<ReturnType>>();
+ auto future = promise->get_future();
+ uv_work_t *work = new uv_work_t;
+
+ struct TaskContext {
+ std::function<ReturnType()> task;
+ std::shared_ptr<std::promise<ReturnType>> promise;
+ };
+
+ TaskContext *p = new TaskContext{ [function = std::forward<Function>(function), ...args = std::forward<Args>(args)]() mutable {
+ return std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
+ }, promise};
+
+ work->data = p;
+ uv_queue_work(loop, work,
+ [](uv_work_t* work) { Q_UNUSED(work) },
+ [](uv_work_t* work, int status) {
+ Q_UNUSED(status)
+ auto* ctx = static_cast<TaskContext*>(work->data);
+ ctx->promise->set_value(ctx->task());
+ delete ctx;
+ delete work;
+ }
+ );
+ return future.get();
+}
+
+// 不要在Js线程中调用
+template <typename ReturnType, typename Function, typename... Args>
+auto runOnJsUIThreadWithPromise(Function&& function, Args&&... args)
+ -> ReturnType
+{
+ if (std::this_thread::get_id() == uiThreadId()) {
+ LOGW("Could not call runOnJsUIThreadWithPromise in js thread");
+ if constexpr (std::is_void_v<ReturnType>) {
+ return;
+ } else {
+ return ReturnType{};
+ }
+ }
+ auto promise = std::make_shared<std::promise<ReturnType>>();
+ auto future = promise->get_future();
+
+ uv_loop_t* loop = nullptr;
+ napi_get_uv_event_loop(uiEnv(), &loop);
+ if (!loop) {
+ if constexpr (std::is_void_v<ReturnType>) {
+ promise->set_value();
+ } else {
+ promise->set_value(ReturnType{});
+ }
+ return future.get();
+ }
+
+ struct TaskContext {
+ std::function<void(std::shared_ptr<std::promise<ReturnType>> promise)> task;
+ std::shared_ptr<std::promise<ReturnType>> promise;
+ };
+
+ TaskContext *p = new TaskContext{
+ [function = std::forward<Function>(function), ...args = std::forward<Args>(args)](auto promise) mutable {
+ std::invoke(std::forward<Function>(function), promise, std::forward<Args>(args)...);
+ }, promise};
+
+ uv_work_t* work = new uv_work_t;
+ work->data = p;
+
+ uv_queue_work(loop, work,
+ [](uv_work_t* work) { Q_UNUSED(work) },
+ [](uv_work_t* work, int status) {
+ Q_UNUSED(status)
+ auto* ctx = static_cast<TaskContext*>(work->data);
+ ctx->task(ctx->promise);
+ delete ctx;
+ delete work;
+ }
+ );
+
+ return future.get();
+}
+}
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONY_P_H
new file mode 100644
@@ -0,0 +1,107 @@
+#ifndef QOPENHARMONYDEFINES_H
+#define QOPENHARMONYDEFINES_H
+#include <hilog/log.h>
+
+#define QPA_LOG_DOMAIN 0xff11
+#define QPA_LOG_TAG "QPAForOpenHarmony"
+#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__))
+#define LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__))
+#define LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__))
+#define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, QPA_LOG_DOMAIN, QPA_LOG_TAG, __VA_ARGS__))
+
+#define NAPI_RETVAL_NOTHING
+
+#define GET_AND_THROW_LAST_ERROR(env, status, function) \
+ do { \
+ const napi_extended_error_info* errorInfo = nullptr; \
+ napi_get_last_error_info((env), &errorInfo); \
+ bool isPending = false; \
+ napi_is_exception_pending((env), &isPending); \
+ if (!isPending && errorInfo != nullptr) { \
+ const char* errorMessage = \
+ errorInfo->error_message != nullptr ? errorInfo->error_message : function; \
+ LOGE("call method %{public}s failed, the error code is %{public}d", errorMessage, status); \
+ } \
+ } while (0)
+
+#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \
+ do { \
+ if (!(assertion)) { \
+ napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \
+ return retVal; \
+ } \
+ } while (0)
+
+#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr)
+
+#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING)
+
+#define NAPI_CALL_BASE(env, theCall, retVal) \
+ do { \
+ napi_status status = (theCall); \
+ if (status != napi_ok) { \
+ GET_AND_THROW_LAST_ERROR((env), status, #theCall); \
+ return retVal; \
+ } \
+ } while (0)
+
+#define NAPI_CALL_BASE_NO_THROW(env, theCall, retVal) \
+ do { \
+ if ((theCall) != napi_ok) { \
+ return retVal; \
+ } \
+ } while (0)
+
+#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr)
+
+#define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING)
+
+#define NAPI_CALL_RETURN_VOID_NO_THROW(env, theCall) NAPI_CALL_BASE_NO_THROW(env, theCall, NAPI_RETVAL_NOTHING)
+
+#define DECLARE_NAPI_PROPERTY(name, val) \
+ { \
+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr \
+ }
+
+#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \
+ { \
+ (name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr \
+ }
+
+#define DECLARE_NAPI_FUNCTION(name, func) \
+ { \
+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \
+ }
+
+#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \
+ { \
+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data \
+ }
+
+#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \
+ { \
+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \
+ }
+
+#define DECLARE_NAPI_GETTER(name, getter) \
+ { \
+ (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \
+ }
+
+#define DECLARE_NAPI_SETTER(name, setter) \
+ { \
+ (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \
+ }
+
+#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \
+ { \
+ (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \
+ }
+
+#define DECLARE_NAPI_FUNCTION(name, func) \
+ { \
+ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \
+ }
+
+
+#endif // QOPENHARMONYDEFINES_H
new file mode 100644
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qpermissions.h"
+#include "qpermissions_p.h"
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qfuture.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ // Todo
+ return Qt::PermissionStatus::Undetermined;
+ }
+
+ void requestPermission(const QPermission &permission,
+ const QPermissions::Private::PermissionCallback &callback)
+ {
+ // Todo
+ }
+}
+
+QT_END_NAMESPACE
@@ -122,6 +122,12 @@ public:
}
#endif // QT_CONFIG(systemsemaphore)
+#ifdef Q_OS_OPENHARMONY
+ struct SharedSegmentMetaData {
+ unsigned int linkCounter;
+ };
+#endif
+
private:
#ifdef Q_OS_WIN
Qt::HANDLE hand = nullptr;
@@ -130,6 +136,10 @@ private:
#else
key_t unix_key = 0;
#endif
+
+#ifdef Q_OS_OPENHARMONY
+ SharedSegmentMetaData *metaDataPtr { nullptr };
+#endif
};
QT_END_NAMESPACE
@@ -25,6 +25,59 @@
QT_BEGIN_NAMESPACE
+#ifdef Q_OS_OPENHARMONY
+namespace {
+
+QByteArray getMetaDataKey(QString key)
+{
+ return QFile::encodeName(
+ QSharedMemoryPrivate::makePlatformSafeKey(key + QStringLiteral("_qt_metadata")));
+}
+
+QSharedMemoryPrivate::SharedSegmentMetaData *getMetaDataPtr(QString key)
+{
+ using SharedSegmentMetaData = QSharedMemoryPrivate::SharedSegmentMetaData;
+
+ int fd;
+ QT_EINTR_LOOP(fd, ::shm_open(getMetaDataKey(key).constData(), O_RDWR | O_CREAT | O_CLOEXEC, 0600));
+ if (fd == -1) {
+ return nullptr;
+ }
+
+ int ftruncateRes;
+ QT_EINTR_LOOP(ftruncateRes, QT_FTRUNCATE(fd, sizeof(SharedSegmentMetaData)));
+ if (ftruncateRes == -1) {
+ qt_safe_close(fd);
+ return nullptr;
+ }
+
+ void *memory = QT_MMAP(0, sizeof(SharedSegmentMetaData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (memory == MAP_FAILED || memory == nullptr) {
+ qt_safe_close(fd);
+ return nullptr;
+ }
+
+ qt_safe_close(fd);
+
+ return reinterpret_cast<SharedSegmentMetaData *>(memory);
+}
+
+bool unlinkSharedMemory(QString key)
+{
+ const QByteArray shmKey = QFile::encodeName(QSharedMemoryPrivate::makePlatformSafeKey(key));
+ return ::shm_unlink(shmKey.constData()) == 0 || errno == ENOENT;
+}
+
+bool unlinkSharedMemoryMetaData(QString key)
+{
+ const QByteArray shmMetaDataKey = getMetaDataKey(key);
+ return ::shm_unlink(shmMetaDataKey.constData()) == 0 || errno == ENOENT;
+}
+
+}
+#endif
+
+
using namespace Qt::StringLiterals;
int QSharedMemoryPrivate::handle()
@@ -142,6 +195,19 @@ bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
return false;
}
+#ifdef Q_OS_OPENHARMONY
+ metaDataPtr = getMetaDataPtr(this->key);
+ if (metaDataPtr == nullptr) {
+ setErrorString(QLatin1String("QSharedMemory::attach (getMetaDataPtr)"));
+ cleanHandle();
+ memory = 0;
+ size = 0;
+ return false;
+ }
+
+ ++(metaDataPtr->linkCounter);
+#endif
+
#ifdef F_ADD_SEALS
// Make sure the shared memory region will not shrink
// otherwise someone could cause SIGBUS on us.
@@ -188,6 +254,25 @@ bool QSharedMemoryPrivate::detach()
// On non-QNX systems (tested Linux and Haiku), the st_nlink field is always 1,
// so we'll simply leak the shared memory files.
cleanHandle();
+
+#ifdef Q_OS_OPENHARMONY
+ const auto linkCounter = --(metaDataPtr->linkCounter);
+ if (linkCounter == 0) {
+ if (!unlinkSharedMemory(this->key)) {
+ setErrorString(QLatin1String("QSharedMemory::detach (shm unlink)"));
+ }
+
+ if (!unlinkSharedMemoryMetaData(this->key)) {
+ setErrorString(QLatin1String("QSharedMemory::detach (shm metadata unlink)"));
+ }
+ }
+
+ if (::munmap(metaDataPtr, sizeof(*metaDataPtr)) == -1) {
+ setErrorString(QLatin1String("QSharedMemory::detach (metadata munmap)"));
+ return false;
+ }
+#endif
+
#endif
return true;
@@ -154,7 +154,7 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
QDirIterator plugins(path,
#if defined(Q_OS_WIN)
QStringList(QStringLiteral("*.dll")),
-#elif defined(Q_OS_ANDROID)
+#elif defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
QStringList("libplugins_%1_*.so"_L1.arg(suffix)),
#endif
QDir::Files);
@@ -241,7 +241,7 @@ void QFactoryLoader::update()
const QStringList paths = QCoreApplication::libraryPaths();
for (const QString &pluginDir : paths) {
-#ifdef Q_OS_ANDROID
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
QString path = pluginDir;
#else
QString path = pluginDir + d->suffix;
@@ -299,7 +299,7 @@ QFactoryLoader::QFactoryLoader(const char *iid,
#if QT_CONFIG(library)
d->cs = cs;
d->suffix = suffix;
-# ifdef Q_OS_ANDROID
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
if (!d->suffix.isEmpty() && d->suffix.at(0) == u'/')
d->suffix.remove(0, 1);
# endif
@@ -249,7 +249,7 @@ static QString locatePlugin(const QString& fileName)
for (const QString &path : std::as_const(paths)) {
for (const QString &prefix : std::as_const(prefixes)) {
for (const QString &suffix : std::as_const(suffixes)) {
-#ifdef Q_OS_ANDROID
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
{
QString pluginPath = basePath + prefix + baseName + suffix;
const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
new file mode 100644
@@ -0,0 +1,273 @@
+#include "qvariant.h"
+#include "qlocale_p.h"
+#include "qdatetime.h"
+#include "qstringlist.h"
+#include "qreadwritelock.h"
+#include "qstringbuilder.h"
+#include "qcoreapplication.h"
+
+#include "qjsmodule.h"
+#include "qopenharmonydefines.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SYSTEMLOCALE
+struct QSystemLocaleData
+{
+ QSystemLocaleData()
+ : lc_numeric(QLocale::C)
+ , lc_time(QLocale::C)
+ , lc_monetary(QLocale::C)
+ , lc_messages(QLocale::C)
+ , m_ohSystemi18n(nullptr)
+ , m_inited{ false }
+ {
+ readEnvironment();
+ }
+
+ void readEnvironment();
+
+ QReadWriteLock lock;
+
+ QLocale lc_numeric;
+ QLocale lc_time;
+ QLocale lc_monetary;
+ QLocale lc_messages;
+ QByteArray lc_messages_var;
+ QByteArray lc_measurement_var;
+ QByteArray lc_collate_var;
+ QStringList uiLanguages;
+ QAtomicInteger<bool> m_inited;
+ QScopedPointer<QJsObject> m_ohSystemi18n;
+};
+
+void QSystemLocaleData::readEnvironment()
+{
+ QWriteLocker locker(&lock);
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if (m_ohSystemi18n.isNull()) {
+ QJsModule module("@ohos.i18n");
+ Napi::Value value = module.get("System");
+ m_ohSystemi18n.reset(new QJsObject(value.As<Napi::Object>()));
+ }
+
+ Napi::Value value = m_ohSystemi18n->call("getSystemLocale");
+ QString tmpAll("C");
+ if (!value.IsNull()) {
+ std::string str = value.ToString();
+ tmpAll = QString::fromStdString(str);
+ }
+
+ QString tmpLanguage("C");
+ value = m_ohSystemi18n->call("getSystemLanguage");
+ if (!value.IsNull()) {
+ std::string str = value.ToString();
+ tmpLanguage = QString::fromStdString(str);
+ }
+
+ QByteArray all = tmpAll.toLocal8Bit();
+ QByteArray language = tmpLanguage.toLocal8Bit();
+
+ QByteArray numeric = all.isEmpty() ? language : all;
+ QByteArray time = all.isEmpty() ? language : all;
+ QByteArray monetary = all.isEmpty() ? language : all;
+ lc_messages_var = all.isEmpty() ? language : all;
+ lc_measurement_var = all.isEmpty() ? language : all;
+ lc_collate_var = all.isEmpty() ? language : all;
+ QByteArray lang = language;
+ if (lang.isEmpty())
+ lang = QByteArray("C");
+ if (numeric.isEmpty())
+ numeric = lang;
+ if (time.isEmpty())
+ time = lang;
+ if (monetary.isEmpty())
+ monetary = lang;
+ if (lc_messages_var.isEmpty())
+ lc_messages_var = lang;
+ if (lc_measurement_var.isEmpty())
+ lc_measurement_var = lang;
+ if (lc_collate_var.isEmpty())
+ lc_collate_var = lang;
+ lc_numeric = QLocale(QString::fromLatin1(numeric));
+ lc_time = QLocale(QString::fromLatin1(time));
+ lc_monetary = QLocale(QString::fromLatin1(monetary));
+ lc_messages = QLocale(QString::fromLatin1(lc_messages_var));
+ });
+ m_inited.storeRelease(true);
+}
+
+Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData)
+#endif
+
+#ifndef QT_NO_SYSTEMLOCALE
+
+QLocale QSystemLocale::fallbackLocale() const
+{
+ QLocale locale(QLocale::C);
+ QtOh::runOnJsUIThreadAndWait([&locale]{
+ Napi::Value value = qSystemLocaleData()->m_ohSystemi18n->call("getSystemLocale");
+ QString lang = QString::fromStdString(std::string(value.As<Napi::String>()));
+ if (lang.isEmpty()) {
+ value = qSystemLocaleData()->m_ohSystemi18n->call("getSystemLanguage");
+ lang = QString::fromStdString(std::string(value.As<Napi::String>()));
+ }
+
+ if (lang.isEmpty()) {
+ value = qSystemLocaleData()->m_ohSystemi18n->call("getSystemRegion");
+ lang = QString::fromStdString(std::string(value.As<Napi::String>()));
+ }
+ // if the locale is the "C" locale, then we can return the language we found here:
+ if (lang.isEmpty() || lang == QLatin1String("C") || lang == QLatin1String("POSIX"))
+ locale = QLocale(lang);
+
+ value = qSystemLocaleData()->m_ohSystemi18n->call("getAppPreferredLanguage");
+ QString prefer = QString::fromStdString(std::string(value.As<Napi::String>()));
+ if (!prefer.isEmpty())
+ locale = QLocale(prefer);
+ });
+
+ return locale;
+}
+
+QVariant QSystemLocale::query(QueryType type, QVariant &&in) const
+{
+ QSystemLocaleData *d = qSystemLocaleData();
+
+ if (type == LocaleChanged) {
+ d->readEnvironment();
+ return QVariant();
+ }
+
+ if (!d->m_inited.loadAcquire())
+ d->readEnvironment();
+
+ QReadLocker locker(&d->lock);
+
+ const QLocale &lc_numeric = d->lc_numeric;
+ const QLocale &lc_time = d->lc_time;
+ const QLocale &lc_monetary = d->lc_monetary;
+ const QLocale &lc_messages = d->lc_messages;
+
+ switch (type) {
+ case DecimalPoint:
+ return lc_numeric.decimalPoint();
+ case GroupSeparator:
+ return lc_numeric.groupSeparator();
+ case ZeroDigit:
+ return lc_numeric.zeroDigit();
+ case NegativeSign:
+ return lc_numeric.negativeSign();
+ case DateFormatLong:
+ return lc_time.dateFormat(QLocale::LongFormat);
+ case DateFormatShort:
+ return lc_time.dateFormat(QLocale::ShortFormat);
+ case TimeFormatLong:
+ return lc_time.timeFormat(QLocale::LongFormat);
+ case TimeFormatShort:
+ return lc_time.timeFormat(QLocale::ShortFormat);
+ case DayNameLong:
+ return lc_time.dayName(in.toInt(), QLocale::LongFormat);
+ case DayNameShort:
+ return lc_time.dayName(in.toInt(), QLocale::ShortFormat);
+ case MonthNameLong:
+ return lc_time.monthName(in.toInt(), QLocale::LongFormat);
+ case MonthNameShort:
+ return lc_time.monthName(in.toInt(), QLocale::ShortFormat);
+ case StandaloneMonthNameLong:
+ return lc_time.standaloneMonthName(in.toInt(), QLocale::LongFormat);
+ case StandaloneMonthNameShort:
+ return lc_time.standaloneMonthName(in.toInt(), QLocale::ShortFormat);
+ case DateToStringLong:
+ return lc_time.toString(in.toDate(), QLocale::LongFormat);
+ case DateToStringShort:
+ return lc_time.toString(in.toDate(), QLocale::ShortFormat);
+ case TimeToStringLong:
+ return lc_time.toString(in.toTime(), QLocale::LongFormat);
+ case TimeToStringShort:
+ return lc_time.toString(in.toTime(), QLocale::ShortFormat);
+ case DateTimeFormatLong:
+ return lc_time.dateTimeFormat(QLocale::LongFormat);
+ case DateTimeFormatShort:
+ return lc_time.dateTimeFormat(QLocale::ShortFormat);
+ case DateTimeToStringLong:
+ return lc_time.toString(in.toDateTime(), QLocale::LongFormat);
+ case DateTimeToStringShort:
+ return lc_time.toString(in.toDateTime(), QLocale::ShortFormat);
+ case PositiveSign:
+ return lc_numeric.positiveSign();
+ case AMText:
+ return lc_time.amText();
+ case PMText:
+ return lc_time.pmText();
+ case FirstDayOfWeek:
+ return lc_time.firstDayOfWeek();
+ case CurrencySymbol:
+ return lc_monetary.currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
+ case CurrencyToString: {
+ switch (in.userType()) {
+ case QMetaType::Int:
+ return lc_monetary.toCurrencyString(in.toInt());
+ case QMetaType::UInt:
+ return lc_monetary.toCurrencyString(in.toUInt());
+ case QMetaType::Double:
+ return lc_monetary.toCurrencyString(in.toDouble());
+ case QMetaType::LongLong:
+ return lc_monetary.toCurrencyString(in.toLongLong());
+ case QMetaType::ULongLong:
+ return lc_monetary.toCurrencyString(in.toULongLong());
+ default:
+ break;
+ }
+ return QString();
+ }
+ case MeasurementSystem: {
+ const QString meas_locale = QString::fromLatin1(d->lc_measurement_var);
+ if (meas_locale.compare(QLatin1String("Metric"), Qt::CaseInsensitive) == 0)
+ return QLocale::MetricSystem;
+ if (meas_locale.compare(QLatin1String("Other"), Qt::CaseInsensitive) == 0)
+ return QLocale::MetricSystem;
+ return QVariant((int)QLocale(meas_locale).measurementSystem());
+ }
+ case Collation:
+ return QString::fromLatin1(d->lc_collate_var);
+ case UILanguages: {
+ if (!d->uiLanguages.isEmpty())
+ return d->uiLanguages;
+ QtOh::runOnJsUIThreadAndWait([d]{
+ Napi::Value value = qSystemLocaleData()->m_ohSystemi18n->call("getSystemLanguages");
+ Napi::Array lst = value.As<Napi::Array>();
+ if (lst.IsNull())
+ return;
+
+ for (uint32_t i = 0; i < lst.Length(); ++i) {
+ Napi::Value value = lst[i];
+ const QString &name = QString::fromStdString(std::string(value.As<Napi::String>()));
+ QStringView lang, script, cntry;
+ if (qt_splitLocaleName(name, &lang, &script, &cntry)) {
+ if (!cntry.length())
+ d->uiLanguages.append(lang.toString());
+ else
+ d->uiLanguages.append(lang.toString() % QLatin1Char('-') % cntry);
+ }
+ }
+ d->uiLanguages.removeDuplicates();
+ });
+ return d->uiLanguages.isEmpty() ? QVariant() : QVariant(d->uiLanguages);
+ }
+ case StringToStandardQuotation:
+ return lc_messages.quoteString(qvariant_cast<QStringView>(in));
+ case StringToAlternateQuotation:
+ return lc_messages.quoteString(qvariant_cast<QStringView>(in), QLocale::AlternateQuotation);
+ case ListToSeparatedString:
+ return lc_messages.createSeparatedList(in.toStringList());
+ case LocaleChanged:
+ Q_ASSERT(false);
+ default:
+ break;
+ }
+ return QVariant();
+}
+#endif // QT_NO_SYSTEMLOCALE
+
+QT_END_NAMESPACE
@@ -272,7 +272,7 @@ void terminate_on_exception(T &&t)
void *QThreadPrivate::start(void *arg)
{
-#ifdef PTHREAD_CANCEL_DISABLE
+#if defined(PTHREAD_CANCEL_DISABLE) && !defined(Q_OS_OPENHARMONY)
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
pthread_cleanup_push(QThreadPrivate::finish, arg);
@@ -314,7 +314,7 @@ void *QThreadPrivate::start(void *arg)
#endif
emit thr->started(QThread::QPrivateSignal());
-#ifdef PTHREAD_CANCEL_DISABLE
+#if defined(PTHREAD_CANCEL_DISABLE) && !defined(Q_OS_OPENHARMONY)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_testcancel();
#endif
@@ -745,7 +745,7 @@ void QThread::start(Priority priority)
void QThread::terminate()
{
-#if !defined(Q_OS_ANDROID)
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY)
Q_D(QThread);
QMutexLocker locker(&d->mutex);
@@ -788,7 +788,7 @@ void QThread::setTerminationEnabled(bool enabled)
"Current thread was not started with QThread.");
Q_UNUSED(thr);
-#if defined(Q_OS_ANDROID)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
Q_UNUSED(enabled);
#else
pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, nullptr);
@@ -33,6 +33,8 @@ static QTimeZonePrivate *newBackendTimeZone()
return new QMacTimeZonePrivate();
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate();
+#elif defined(Q_OS_OPENHARMONY)
+ return new QOhTimeZonePrivate();
#elif defined(Q_OS_UNIX)
return new QTzTimeZonePrivate();
#elif QT_CONFIG(icu)
@@ -60,6 +62,8 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId)
return new QMacTimeZonePrivate(ianaId);
#elif defined(Q_OS_ANDROID)
return new QAndroidTimeZonePrivate(ianaId);
+#elif defined(Q_OS_OPENHARMONY)
+ return new QOhTimeZonePrivate(ianaId);
#elif defined(Q_OS_UNIX)
return new QTzTimeZonePrivate(ianaId);
#elif QT_CONFIG(icu)
new file mode 100644
@@ -0,0 +1,210 @@
+#include "qtimezone.h"
+#include "qdatetime.h"
+#include "qtimezoneprivate_p.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QOhTimeZonePrivate::QOhTimeZonePrivate(const QString &jsModuleName, const QTimeZonePrivate &parent)
+ : QTimeZonePrivate(parent), ohI18nModule(jsModuleName), ohTimeZone(nullptr)
+{
+}
+
+// Create the system default time zone
+QOhTimeZonePrivate::QOhTimeZonePrivate()
+ : QOhTimeZonePrivate(QString("@ohos.i18n"))
+{
+ m_id = QtOh::runOnJsUIThreadWithPromise<QByteArray>([this](auto p) {
+ QJsModule jsSystemDateTime("@ohos.systemDateTime");
+ QJsPromise promise(jsSystemDateTime.call("getTimezone").As<Napi::Promise>());
+ promise.onThen([this, p](const Napi::CallbackInfo &info){
+ p->set_value(QByteArray::fromStdString(info[0].ToString()));
+ }).onCatch([this, p](const Napi::CallbackInfo &info) {
+ p->set_value(QByteArray());
+ });
+ });
+}
+
+// Create a named time zone
+QOhTimeZonePrivate::QOhTimeZonePrivate(const QByteArray &ianaId)
+ : QOhTimeZonePrivate(QString("@ohos.i18n"))
+{
+ init(ianaId);
+}
+
+QOhTimeZonePrivate::QOhTimeZonePrivate(const QOhTimeZonePrivate &other)
+ : QOhTimeZonePrivate(QString("@ohos.i18n"), other)
+{
+ ohTimeZone.reset(new QJsObject(other.ohTimeZone.data()->object()));
+ m_id = other.id();
+}
+
+QOhTimeZonePrivate::~QOhTimeZonePrivate()
+{
+}
+
+void QOhTimeZonePrivate::init(const QByteArray &ianaId)
+{
+ QtOh::runOnJsUIThreadAndWait([=, this] {
+ const QString iana = QString::fromUtf8(ianaId);
+ Napi::Object timeZone =
+ ohI18nModule.call("getTimeZone", { Napi::String::New(QtOh::uiEnv(), iana.toStdString()) }).As<Napi::Object>();
+
+ if (timeZone.Has("getID")) {
+ QString timezoneId = QString::fromStdString(QJsObject(timeZone).call("getID").As<Napi::String>());
+ if (timezoneId != QStringLiteral("Etc/Unknown")) {
+ m_id = ianaId;
+ ohTimeZone.reset(new QJsObject(timeZone));
+ }
+ }
+ });
+}
+
+QOhTimeZonePrivate *QOhTimeZonePrivate::clone() const
+{
+ return new QOhTimeZonePrivate(*this);
+}
+
+
+QString QOhTimeZonePrivate::displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
+ const QLocale &locale) const
+{
+ Q_UNUSED(nameType);
+ Q_UNUSED(locale);
+
+ if(ohTimeZone.isNull())
+ return QString();
+
+ return QtOh::runOnJsUIThreadWithResult([=, this] {
+ bool daylightTime = (timeType == QTimeZone::DaylightTime);
+ return QString::fromStdString(ohTimeZone->call("getDisplayName",
+ { Napi::Boolean::New(QtOh::uiEnv(), daylightTime) }).As<Napi::String>());
+ });
+}
+
+QString QOhTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
+{
+ if (isDaylightTime(atMSecsSinceEpoch))
+ return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QLocale());
+ else
+ return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QLocale());
+}
+
+int QOhTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
+{
+ if (!ohTimeZone.isNull()) {
+ return QtOh::runOnJsUIThreadWithResult([=, this] {
+ return ohTimeZone->call("getOffset",
+ { Napi::Number::New(QtOh::uiEnv(), atMSecsSinceEpoch) })
+ .As<Napi::Number>().Int32Value() / 1000;
+ });
+ } else {
+ return 0;
+ }
+}
+
+int QOhTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
+{
+ Q_UNUSED(atMSecsSinceEpoch);
+
+ if (!ohTimeZone.isNull()) {
+ return QtOh::runOnJsUIThreadWithResult([=, this] {
+ return ohTimeZone->call("getRawOffset").As<Napi::Number>().Int32Value() / 1000;
+ });
+ } else {
+ return 0;
+ }
+}
+
+int QOhTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
+{
+ return (offsetFromUtc(atMSecsSinceEpoch) - standardTimeOffset(atMSecsSinceEpoch));
+}
+
+bool QOhTimeZonePrivate::hasDaylightTime() const
+{
+ // OpenHarmony没有接口直接判断,通过XXXX.1.1与XXXX.7.1的偏移是否相同来判断
+ if (!ohTimeZone.isNull()) {
+ int currentYear = QDateTime::currentDateTime().date().year();
+ QDate jan1(currentYear, 1, 1);
+ QDateTime jan1DateTime = QDateTime(jan1, QTime(0, 0, 0), Qt::UTC);
+ QDate jul1(currentYear, 7, 1);
+ QDateTime jul1DateTime = QDateTime(jul1, QTime(0, 0, 0), Qt::UTC);
+ qint64 jan1TimeStamp = jan1DateTime.toMSecsSinceEpoch();
+ qint64 jul1TimeStamp = jul1DateTime.toMSecsSinceEpoch();
+ return offsetFromUtc(jan1TimeStamp) != offsetFromUtc(jul1TimeStamp);
+ } else {
+ return false;
+ }
+}
+
+bool QOhTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
+{
+ if (!ohTimeZone.isNull()) {
+ return standardTimeOffset(atMSecsSinceEpoch) != offsetFromUtc(atMSecsSinceEpoch);
+ } else {
+ return false;
+ }
+}
+
+QTimeZonePrivate::Data QOhTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
+{
+ if (!ohTimeZone.isNull()) {
+ Data data;
+ data.atMSecsSinceEpoch = forMSecsSinceEpoch;
+ data.standardTimeOffset = standardTimeOffset(forMSecsSinceEpoch);
+ data.offsetFromUtc = offsetFromUtc(forMSecsSinceEpoch);
+ data.daylightTimeOffset = data.offsetFromUtc - data.standardTimeOffset;
+ data.abbreviation = abbreviation(forMSecsSinceEpoch);
+ return data;
+ } else {
+ return invalidData();
+ }
+}
+
+bool QOhTimeZonePrivate::hasTransitions() const
+{
+ //TODO:api20有接口可以实现
+ return false;
+}
+
+QTimeZonePrivate::Data QOhTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
+{
+ // transitions not available on OpenHarmony, so return an invalid data object
+ Q_UNUSED(afterMSecsSinceEpoch);
+ return invalidData();
+}
+
+QTimeZonePrivate::Data QOhTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
+{
+ // transitions not available on OpenHarmony, so return an invalid data object
+ Q_UNUSED(beforeMSecsSinceEpoch);
+ return invalidData();
+}
+
+QByteArray QOhTimeZonePrivate::systemTimeZoneId() const
+{
+ return m_id;
+}
+
+QList<QByteArray> QOhTimeZonePrivate::availableTimeZoneIds() const
+{
+ QList<QByteArray> availableTimeZoneIdList;
+
+ if (!ohTimeZone.isNull()) {
+ QtOh::runOnJsUIThreadAndWait([&] {
+ Napi::Array availableIDs = ohTimeZone->call("getAvailableIDs").As<Napi::Array>();
+ for (uint32_t i = 0; i < availableIDs.Length(); i++) {
+ availableTimeZoneIdList.append(
+ QByteArray::fromStdString(availableIDs.Get(i).ToString()));
+ }
+ });
+ }
+
+ return availableTimeZoneIdList;
+}
+
+QT_END_NAMESPACE
@@ -242,7 +242,7 @@ private:
};
#endif
-#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENHARMONY)
struct QTzTransitionTime
{
qint64 atMSecsSinceEpoch;
@@ -475,6 +475,54 @@ private:
};
#endif // Q_OS_ANDROID
+#if defined(Q_OS_OPENHARMONY)
+#include "qjsmodule.h"
+#include <QScopedPointer>
+
+class QOhTimeZonePrivate final : public QTimeZonePrivate
+{
+public:
+ // Create default time zone
+ QOhTimeZonePrivate();
+ // Create named time zone
+ QOhTimeZonePrivate(const QByteArray &ianaId);
+ QOhTimeZonePrivate(const QOhTimeZonePrivate &other);
+ // Delegate constructor
+ QOhTimeZonePrivate(const QString &jsModuleName, const QTimeZonePrivate &parent = QTimeZonePrivate());
+ ~QOhTimeZonePrivate();
+
+ QOhTimeZonePrivate *clone() const override;
+
+ QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
+ const QLocale &locale) const override;
+ QString abbreviation(qint64 atMSecsSinceEpoch) const override;
+
+ int offsetFromUtc(qint64 atMSecsSinceEpoch) const override;
+ int standardTimeOffset(qint64 atMSecsSinceEpoch) const override;
+ int daylightTimeOffset(qint64 atMSecsSinceEpoch) const override;
+
+ bool hasDaylightTime() const override;
+ bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
+
+ Data data(qint64 forMSecsSinceEpoch) const override;
+
+ bool hasTransitions() const override;
+ Data nextTransition(qint64 afterMSecsSinceEpoch) const override;
+ Data previousTransition(qint64 beforeMSecsSinceEpoch) const override;
+
+ QByteArray systemTimeZoneId() const override;
+
+ QList<QByteArray> availableTimeZoneIds() const override;
+
+private:
+ void init(const QByteArray &zoneId);
+
+ QJsModule ohI18nModule;
+ QScopedPointer<QJsObject> ohTimeZone;
+};
+#endif // Q_OS_OPENHARMONY
+
+
QT_END_NAMESPACE
#endif // QTIMEZONEPRIVATE_P_H
@@ -25,6 +25,8 @@ if (QT_FEATURE_gui)
set(_default_platform "haiku")
elseif(WASM)
set(_default_platform "wasm")
+ elseif(OHOS)
+ set(_default_platform "openharmony")
else()
set(_default_platform "xcb")
endif()
@@ -689,6 +691,22 @@ qt_internal_extend_target(Gui CONDITION UNIX AND NOT APPLE
text/unix/qgenericunixfontdatabase_p.h
)
+qt_internal_extend_target(Gui CONDITION UNIX AND OHOS
+ SOURCES
+ text/openharmony/qohfontdatabase_p.h
+ text/openharmony/qohfontdatabase.cpp
+ kernel/qplatformaccessctrl.h
+ kernel/qplatformaccessctrl.cpp
+ kernel/qplatformabilityctrl.h
+ kernel/qplatformabilityctrl.cpp
+ kernel/qohosabilityctrl.h
+ kernel/qohosabilityctrl.cpp
+ platform/openharmony/qohnativeinterface.cpp
+ LIBRARIES
+ native_drawing
+ hitrace_ndk.z
+)
+
qt_internal_extend_target(Gui CONDITION QT_FEATURE_fontconfig AND QT_FEATURE_freetype AND UNIX AND NOT APPLE
SOURCES
text/unix/qfontconfigdatabase.cpp text/unix/qfontconfigdatabase_p.h
@@ -422,6 +422,7 @@ qt_config_compile_test(opengles3
LABEL "OpenGL ES 3.0"
LIBRARIES
${test_libs}
+ GLESv3
# special case begin
COMPILE_OPTIONS ${extra_compiler_options}
# special case end
@@ -141,6 +141,32 @@ QClipboard::QClipboard(QObject *parent)
QClipboard::~QClipboard()
{
}
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+void QClipboard::reportProgress()
+{
+ if (progressMode() == QClipboard::ProgressMode::Default)
+ return;
+
+ QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard();
+ clipboard->reportProgress();
+}
+
+void QClipboard::setProgressMode(ProgressMode mode)
+{
+ if (progressMode() == mode)
+ return;
+
+ QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard();
+ clipboard->setProgressMode(mode);
+ emit progressModeChanged(mode);
+}
+
+QClipboard::ProgressMode QClipboard::progressMode() const
+{
+ QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard();
+ return clipboard->progressMode();
+}
+#endif
/*!
\fn void QClipboard::changed(QClipboard::Mode mode)
@@ -25,6 +25,12 @@ private:
public:
enum Mode { Clipboard, Selection, FindBuffer, LastMode = FindBuffer };
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+ enum ProgressMode { None, Default };
+ void reportProgress();
+ QClipboard::ProgressMode progressMode() const;
+ void setProgressMode(QClipboard::ProgressMode mode);
+#endif
void clear(Mode mode = Clipboard);
@@ -52,6 +58,10 @@ Q_SIGNALS:
void selectionChanged();
void findBufferChanged();
void dataChanged();
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+ void progressValueChanged(int value);
+ void progressModeChanged(QClipboard::ProgressMode mode);
+#endif
protected:
friend class QApplication;
@@ -1867,6 +1867,22 @@ QCloseEvent::QCloseEvent()
Q_IMPL_EVENT_COMMON(QCloseEvent)
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+QCloseEvent::QCloseEvent(CloseReason reason)
+ : QEvent(Close)
+ , m_reason(reason)
+{
+
+}
+
+QCloseEvent::CloseReason QCloseEvent::closeReason() const
+{
+ return m_reason;
+}
+#endif
+#endif
+
/*!
\class QIconDragEvent
\brief The QIconDragEvent class indicates that a main icon drag has begun.
@@ -562,6 +562,23 @@ class Q_GUI_EXPORT QCloseEvent : public QEvent
Q_DECL_EVENT_COMMON(QCloseEvent)
public:
QCloseEvent();
+
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ enum CloseReason {
+ UnKnown,
+ WindowClose,
+ AbilityClose,
+ InternalClose
+ };
+
+ QCloseEvent(CloseReason reason);
+ CloseReason closeReason() const;
+
+private:
+ CloseReason m_reason;
+#endif
+#endif
};
@@ -593,7 +610,11 @@ class Q_GUI_EXPORT QContextMenuEvent : public QInputEvent
{
Q_DECL_EVENT_COMMON(QContextMenuEvent)
public:
- enum Reason { Mouse, Keyboard, Other };
+ enum Reason { Mouse, Keyboard, Other
+#ifdef Q_OS_OPENHARMONY
+ , TouchScreen
+#endif
+ };
QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
@@ -2210,6 +2210,13 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
qWarning("QGuiApplicationPrivate::processMouseEvent: Got NaN in mouse position");
return;
}
+
+#ifdef Q_OS_OPENHARMONY
+ if (e->buttonType == QEvent::MouseButtonPress) {
+ HiTracer tracer("QGuiApplicationPrivate::processMouseEvent DOWN");
+ qInfo("QGuiApplicationPrivate::processMouseEvent DOWN");
+ }
+#endif
type = e->buttonType;
button = e->button;
@@ -2735,11 +2742,24 @@ void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::Cl
e->eventAccepted = false;
return;
}
-
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ QCloseEvent event(e->reason);
+#else
QCloseEvent event;
+#endif
+#else
+ QCloseEvent event;
+#endif
QGuiApplication::sendSpontaneousEvent(e->window.data(), &event);
e->eventAccepted = event.isAccepted();
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ if (e->resolve)
+ e->resolve(e->eventAccepted);
+#endif
+#endif
}
void QGuiApplicationPrivate::processFileOpenEvent(QWindowSystemInterfacePrivate::FileOpenEvent *e)
@@ -2910,7 +2930,11 @@ void QGuiApplicationPrivate::processContextMenuEvent(QWindowSystemInterfacePriva
if (!e->window || e->mouseTriggered || e->window->d_func()->blockedByModalWindow)
return;
+#ifdef Q_OS_OPENHARMONY
+ QContextMenuEvent ev(QContextMenuEvent::TouchScreen, e->pos, e->globalPos, e->modifiers);
+#else
QContextMenuEvent ev(QContextMenuEvent::Keyboard, e->pos, e->globalPos, e->modifiers);
+#endif
QGuiApplication::sendSpontaneousEvent(e->window.data(), &ev);
}
#endif
@@ -3732,6 +3756,13 @@ void QGuiApplicationPrivate::processApplicationTermination(QWindowSystemInterfac
QEvent event(QEvent::Quit);
QGuiApplication::sendSpontaneousEvent(QGuiApplication::instance(), &event);
windowSystemEvent->eventAccepted = event.isAccepted();
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ if (windowSystemEvent->resolve) {
+ windowSystemEvent->resolve(event.isAccepted());
+ }
+#endif
+#endif
}
/*!
@@ -161,6 +161,9 @@ Q_SIGNALS:
QT_DEPRECATED_VERSION_X_6_0("Handle QEvent::ApplicationPaletteChange instead") void paletteChanged(const QPalette &pal);
QT_DEPRECATED_VERSION_X_6_0("Handle QEvent::ApplicationFontChange instead") void fontChanged(const QFont &font);
#endif
+#ifdef Q_OS_OPENHARMONY
+ void newNativeWindowCreated(const QString &want);
+#endif
protected:
bool event(QEvent *) override;
bool compressEvent(QEvent *, QObject *receiver, QPostEventList *) override;
new file mode 100644
@@ -0,0 +1,30 @@
+#include "qohosabilityctrl.h"
+#include <qpa/qplatformintegration.h>
+#include "qguiapplication_p.h"
+#include "qplatformabilityctrl.h"
+
+void QtOhPrivate::startAbility(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void(Napi::Value)> &resultHandler)
+{
+ QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
+ if (QPlatformAbilityCtrl *ctrl = pi->abilityCtrl()) {
+ ctrl->startAbility(want, startOptions, resultHandler);
+ }
+}
+
+void QtOhPrivate::startAbilityForResult(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void (Napi::Value,Napi::Value)> &resultHandler)
+{
+ QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
+ if (QPlatformAbilityCtrl *ctrl = pi->abilityCtrl()) {
+ ctrl->startAbilityForResult(want, startOptions, resultHandler);
+ }
+}
+
+void QtOhPrivate::terminateSelfWithResult(const QVariantMap &abilityResult)
+{
+ QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
+ if (QPlatformAbilityCtrl *ctrl = pi->abilityCtrl()) {
+ ctrl->terminateSelfWithResult(abilityResult);
+ }
+}
new file mode 100644
@@ -0,0 +1,23 @@
+#ifndef QOHOSABILITYCTRL_H
+#define QOHOSABILITYCTRL_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+namespace Napi {
+class Value;
+}
+namespace QtOhPrivate {
+
+Q_GUI_EXPORT void startAbility(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void(Napi::Value)> &resultHandler);
+Q_GUI_EXPORT void startAbilityForResult(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void(Napi::Value, Napi::Value)> &resultHandler);
+Q_GUI_EXPORT void terminateSelfWithResult(const QVariantMap &abilityResult);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QOHOSABILITYCTRL_H
new file mode 100644
@@ -0,0 +1,5 @@
+#include "qplatformabilityctrl.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QPLATFORMABILITYCTRL_H
+#define QPLATFORMABILITYCTRL_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QVariantMap>
+namespace Napi {
+class Value;
+}
+
+QT_BEGIN_NAMESPACE
+
+
+class Q_GUI_EXPORT QPlatformAbilityCtrl
+{
+public:
+ virtual void startAbility(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void (Napi::Value)> &resultHandler) = 0;
+
+ virtual void startAbilityForResult(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void(Napi::Value, Napi::Value)> &resultHandler) = 0;
+
+ virtual void terminateSelfWithResult(const QVariantMap &abilityResult) = 0;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMABILITYCTRL_H
new file mode 100644
@@ -0,0 +1,122 @@
+#include <QDebug>
+#include <QThread>
+#include <QJsObject>
+#include <QSemaphore>
+#include <QCoreApplication>
+
+#include "qplatformaccessctrl.h"
+
+static bool waitForSemaphore(int timeoutMs, QSharedPointer<QSemaphore> sem)
+{
+ while (timeoutMs > 0) {
+ if (sem->tryAcquire(1, 10))
+ return true;
+ timeoutMs -= 10;
+ QCoreApplication::processEvents();
+ }
+ return false;
+}
+
+int QPlatformAccessCtrl::accessTokenId()
+{
+ int tokenId = -1;
+ QJsObject *bundleMgr = bundleManagerAcquire();
+ if (nullptr == bundleMgr) {
+ qWarning() << Q_FUNC_INFO << "bundle manager acquire faild.";
+ return tokenId;
+ }
+
+ QtOh::runOnJsUIThreadAndWait([&tokenId, bundleMgr]{
+ /* NOTE 0x00000001 参考BundleFlag
+ * https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V13/js-apis-bundlemanager-V13#bundleflag
+ */
+ Napi::Value result = bundleMgr->call("getBundleInfoForSelfSync", { Napi::Number::New(bundleMgr->env(), 0x00000001) });
+ Napi::Object bundleInfo = result.ToObject();
+ Napi::Value value = bundleInfo.Get("appInfo").As<Napi::Object>().Get("accessTokenId");
+ if (value.IsNull())
+ return;
+
+ tokenId = value.As<Napi::Number>();
+ });
+
+ return tokenId;
+}
+
+QPlatformAccessCtrl::~QPlatformAccessCtrl()
+{
+
+}
+
+int QPlatformAccessCtrl::checkPermission(const QString &permission)
+{
+ int tokenId = accessTokenId();
+ if (-1 == tokenId) {
+ qWarning() << Q_FUNC_INFO << "get accessTokenId failed.";
+ return -1;
+ }
+
+ QJsObject *atManager = atManagerAcquire();
+ if (nullptr == atManager) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ return -1;
+ }
+
+ int retValue = -1;
+ QtOh::runOnJsUIThreadAndWait([&retValue, permission, tokenId, atManager]{
+ std::string stdPermission(permission.toStdString());
+ Napi::Value result = atManager->call("checkAccessTokenSync",
+ { Napi::Number::New(atManager->env(), tokenId),
+ Napi::String::New(atManager->env(), stdPermission) });
+ if (result.IsNull())
+ return;
+
+ retValue = result.As<Napi::Number>();
+ });
+
+ return retValue;
+}
+
+QtOhPrivate::PermissionRequestResult QPlatformAccessCtrl::requestPermissionsSync(const QStringList &permissions, int timeoutMs)
+{
+ QSharedPointer<QtOhPrivate::PermissionRequestResult> res(new QtOhPrivate::PermissionRequestResult());
+ QSharedPointer<QSemaphore> sem(new QSemaphore());
+ requestPermissions(permissions, [sem, res](const QtOhPrivate::PermissionRequestResult &result) {
+ *res = result;
+ sem->release();
+ });
+
+ if (waitForSemaphore(timeoutMs, sem))
+ return std::move(*res);
+ else
+ return QtOhPrivate::PermissionRequestResult();
+}
+
+QtOhPrivate::PermissionRequestResult QPlatformAccessCtrl::requestPermissionsOnSettingSync(const QStringList &permissions, int timeoutMs)
+{
+ QSharedPointer<QtOhPrivate::PermissionRequestResult> res(new QtOhPrivate::PermissionRequestResult());
+ QSharedPointer<QSemaphore> sem(new QSemaphore());
+ requestPermissionsOnSetting(permissions, [sem, res](const QtOhPrivate::PermissionRequestResult &result) {
+ *res = result;
+ sem->release();
+ });
+
+ if (waitForSemaphore(timeoutMs, sem))
+ return std::move(*res);
+ else
+ return QtOhPrivate::PermissionRequestResult();
+}
+
+bool QPlatformAccessCtrl::requestEnableNotificationSync(int timeoutMs)
+{
+ bool res = false;
+ QSharedPointer<QSemaphore> sem(new QSemaphore());
+ requestEnableNotification([sem, &res](bool result) {
+ res = result;
+ sem->release();
+ });
+
+ if (waitForSemaphore(timeoutMs, sem))
+ return res;
+ else
+ return false;
+}
new file mode 100644
@@ -0,0 +1,63 @@
+#ifndef QPLATFORMACCESSCTRL_H
+#define QPLATFORMACCESSCTRL_H
+
+#include <functional>
+#include <QSharedPointer>
+#include <QtGui/qtguiglobal.h>
+
+class QJsObject;
+QT_BEGIN_NAMESPACE
+
+namespace QtOhPrivate {
+enum class Q_GUI_EXPORT PermissionsResult {
+ Denied = -1,
+ Granted = 0,
+ InValidRequest = 2
+};
+
+struct Q_GUI_EXPORT PermissionRequestResult{
+ QStringList permissions;
+ QList<PermissionsResult> authResults;
+ QList<bool> dialogShownResults;
+
+ PermissionRequestResult() = default;
+ PermissionRequestResult(const QStringList& perms,
+ const QList<PermissionsResult>& auth,
+ const QList<bool>& dialogShown)
+ : permissions(perms), authResults(auth), dialogShownResults(dialogShown) {}
+};
+
+
+using PermissionsResultFunc = std::function<void(const PermissionRequestResult &)>;
+using enableNotificationFunc = std::function<void(bool)>;
+}
+
+class Q_GUI_EXPORT QPlatformAccessCtrl
+{
+public:
+ explicit QPlatformAccessCtrl() = default;
+ virtual ~QPlatformAccessCtrl();
+
+ virtual int checkPermission(const QString &permission);
+
+ QtOhPrivate::PermissionRequestResult requestPermissionsSync(const QStringList &permissions, int timeoutMs = INT_MAX);
+ QtOhPrivate::PermissionRequestResult requestPermissionsOnSettingSync(const QStringList &permissions, int timeoutMs = INT_MAX);
+ virtual void requestPermissions(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc) = 0;
+ virtual void requestPermissionsOnSetting(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc) = 0;
+
+ virtual void requestEnableNotification(const QtOhPrivate::enableNotificationFunc &callbackFunc) = 0;
+ virtual bool requestEnableNotificationSync(int timeoutMs = INT_MAX);
+ virtual bool isNotificationEnabled() = 0;
+ virtual void openNotificationSettings() = 0;
+protected:
+ int accessTokenId();
+ virtual QJsObject *atManagerAcquire() = 0;
+ virtual QJsObject *bundleManagerAcquire() = 0;
+ virtual QJsObject *notificationManagerAcquire() = 0;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QtOhPrivate::PermissionRequestResult)
+
+#endif // QPLATFORMACCESSCTRL_H
@@ -88,7 +88,26 @@ void QPlatformClipboard::emitChanged(QClipboard::Mode mode)
if (!QGuiApplicationPrivate::is_app_closing) // QTBUG-39317, prevent emission when closing down.
QGuiApplication::clipboard()->emitChanged(mode);
}
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+void QPlatformClipboard::reportProgress()
+{
+#warning TODO Handle
+}
+
+QClipboard::ProgressMode QPlatformClipboard::progressMode() const
+{
+#warning TODO Handle
+ return QClipboard::ProgressMode::None;
+}
+void QPlatformClipboard::setProgressMode(QClipboard::ProgressMode mode)
+{
+#warning TODO Handle
+ Q_UNUSED(mode);
+}
+#endif
+#endif
QT_END_NAMESPACE
#endif //QT_NO_CLIPBOARD
@@ -35,6 +35,13 @@ public:
virtual bool supportsMode(QClipboard::Mode mode) const;
virtual bool ownsMode(QClipboard::Mode mode) const;
void emitChanged(QClipboard::Mode mode);
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ virtual void reportProgress();
+ virtual QClipboard::ProgressMode progressMode() const;
+ virtual void setProgressMode(QClipboard::ProgressMode mode);
+#endif
+#endif
};
QT_END_NAMESPACE
@@ -375,6 +375,10 @@ public:
virtual QString selectedMimeTypeFilter() const;
virtual QString selectedNameFilter() const = 0;
+#ifdef Q_OS_OPENHARMONY
+ virtual QList<QUrl> selectedUris() const { return QList<QUrl>(); };
+#endif
+
virtual bool isSupportedUrl(const QUrl &url) const;
const QSharedPointer<QFileDialogOptions> &options() const;
@@ -361,6 +361,18 @@ QPlatformAccessibility *QPlatformIntegration::accessibility() const
#endif
+#ifdef Q_OS_OPENHARMONY
+QPlatformAccessCtrl *QPlatformIntegration::accessCtrl() const
+{
+ return nullptr;
+}
+
+QPlatformAbilityCtrl *QPlatformIntegration::abilityCtrl() const
+{
+ return nullptr;
+}
+#endif
+
QVariant QPlatformIntegration::styleHint(StyleHint hint) const
{
switch (hint) {
@@ -68,7 +68,10 @@ template <typename R, typename I, typename... Args, R(I::*func)(Args...) const>
struct QInterfaceProxy<func> : public QInterfaceProxyImp<R, I, func, Args...> {};
} // QNativeInterface::Private
-
+#ifdef Q_OS_OPENHARMONY
+class QPlatformAccessCtrl;
+class QPlatformAbilityCtrl;
+#endif
class Q_GUI_EXPORT QPlatformIntegration
{
public:
@@ -133,6 +136,11 @@ public:
virtual QPlatformAccessibility *accessibility() const;
#endif
+#ifdef Q_OS_OPENHARMONY
+ virtual QPlatformAccessCtrl *accessCtrl() const;
+ virtual QPlatformAbilityCtrl *abilityCtrl() const;
+#endif
+
// Access native handles. The window handle is already available from Wid;
virtual QPlatformNativeInterface *nativeInterface() const;
@@ -49,6 +49,14 @@ bool QPlatformServices::openDocument(const QUrl &url)
return false;
}
+#ifdef Q_OS_OPENHARMONY
+bool QPlatformServices::openUri(const QUrl &url, bool isDir){
+ qWarning("This plugin does not support QPlatformServices::openUri() for '%s'.",
+ qPrintable(url.toString()));
+ return false;
+}
+#endif
+
/*!
* \brief QPlatformServices::desktopEnvironment returns the active desktop environment.
*
@@ -46,6 +46,10 @@ public:
virtual bool openUrl(const QUrl &url);
virtual bool openDocument(const QUrl &url);
+#ifdef Q_OS_OPENHARMONY
+ virtual bool openUri(const QUrl &url, bool isDir);
+#endif
+
virtual QByteArray desktopEnvironment() const;
virtual bool hasCapability(Capability capability) const;
@@ -143,6 +143,11 @@ QPlatformMenu *QPlatformSystemTrayIcon::createMenu() const
return QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
}
+#ifdef Q_OS_OPENHARMONY
+void QPlatformSystemTrayIcon::updateIcon(const QIcon &iconLight, const QIcon &iconDark) {
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qplatformsystemtrayicon.cpp"
@@ -40,6 +40,9 @@ public:
virtual void init() = 0;
virtual void cleanup() = 0;
+#ifdef Q_OS_OPENHARMONY
+ virtual void updateIcon(const QIcon &iconLight, const QIcon &iconDark);
+#endif
virtual void updateIcon(const QIcon &icon) = 0;
virtual void updateToolTip(const QString &tooltip) = 0;
virtual void updateMenu(QPlatformMenu *menu) = 0;
@@ -847,6 +847,17 @@ QString QPlatformTheme::removeMnemonics(const QString &original)
returnText.truncate(finalDest);
return returnText;
}
+#ifdef Q_OS_OPENHARMONY
+bool QPlatformTheme::darkThemeEnabled() const
+{
+ return false;
+}
+
+void QPlatformTheme::setThemeColorMode(int colorMode)
+{
+ Q_UNUSED(colorMode);
+}
+#endif
unsigned QPlatformThemePrivate::currentKeyPlatforms()
{
@@ -319,7 +319,10 @@ public:
static QString defaultStandardButtonText(int button);
static QString removeMnemonics(const QString &original);
QString name() const;
-
+#ifdef Q_OS_OPENHARMONY
+ virtual bool darkThemeEnabled() const;
+ virtual void setThemeColorMode(int colorMode);
+#endif
protected:
explicit QPlatformTheme(QPlatformThemePrivate *priv);
QScopedPointer<QPlatformThemePrivate> d_ptr;
@@ -71,6 +71,18 @@ QPlatformScreen *QPlatformWindow::screen() const
return scr ? scr->handle() : nullptr;
}
+#ifdef Q_OS_OPENHARMONY
+/*!
+ * \brief obtain the size of xcomponent provided by the harmonyos window
+ * by default, return the size of the window.
+ * \return retur QSize
+ */
+QSize QPlatformWindow::xcomponentSize() const
+{
+ return windowGeometry().size();
+}
+#endif
+
/*!
Returns the actual surface format of the window.
*/
@@ -48,7 +48,9 @@ public:
QPlatformWindow *parent() const;
QPlatformScreen *screen() const override;
-
+#ifdef Q_OS_OPENHARMONY
+ virtual QSize xcomponentSize() const;
+#endif
virtual QSurfaceFormat format() const override;
virtual void setGeometry(const QRect &rect);
@@ -122,6 +122,34 @@ protected:
};
#endif
+#if defined(Q_OS_OPENHARMONY)
+struct Q_GUI_EXPORT QOpenHarmonyWindow
+{
+ QT_DECLARE_NATIVE_INTERFACE(QOpenHarmonyWindow, 1, QWindow)
+
+ enum class NativeNodeRenderFit {
+ FIT_CENTER = 0, /* 保持动画终态的内容大小,并且内容始终与组件保持中心对齐 */
+ FIT_TOP = 1, /* 保持动画终态的内容大小,并且内容始终与组件保持顶部中心对齐 */
+ FIT_BOTTOM = 2, /* 保持动画终态的内容大小,并且内容始终与组件保持底部中心对齐 */
+ FIT_LEFT = 3, /* 保持动画终态的内容大小,并且内容始终与组件保持左侧对齐 */
+ FIT_RIGHT = 4, /* 保持动画终态的内容大小,并且内容始终与组件保持右侧对齐 */
+ FIT_TOP_LEFT = 5, /* 保持动画终态的内容大小,并且内容始终与组件保持左上角对齐 */
+ FIT_TOP_RIGHT = 6, /* 保持动画终态的内容大小,并且内容始终与组件保持右上角对齐 */
+ FIT_BOTTOM_LEFT = 7, /* 保持动画终态的内容大小,并且内容始终与组件保持左下角对齐 */
+ FIT_BOTTOM_RIGHT = 8, /* 保持动画终态的内容大小,并且内容始终与组件保持右下角对齐 */
+ FIT_RESIZE_FILL = 9, /* 不考虑动画终态内容的宽高比,并且内容始终缩放到组件的大小 */
+ FIT_RESIZE_CONTAIN = 10, /* 保持动画终态内容的宽高比进行缩小或放大,使内容完整显示在组件内,且与组件保持中心对齐 */
+ FIT_RESIZE_CONTAIN_TOP_LEFT = 11, /* 保持动画终态内容的宽高比进行缩小或放大,使内容完整显示在组件内。当组件宽方向有剩余时,内容与组件保持左侧对齐,当组件高方向有剩余时,内容与组件保持顶部对齐 */
+ FIT_RESIZE_CONTAIN_BOTTOM_RIGHT = 12, /* 保持动画终态内容的宽高比进行缩小或放大,使内容完整显示在组件内。当组件宽方向有剩余时,内容与组件保持右侧对齐,当组件高方向有剩余时,内容与组件保持底部对齐 */
+ FIT_RESIZE_COVER = 13, /* 保持动画终态内容的宽高比进行缩小或放大,使内容两边都大于或等于组件两边,且与组件保持中心对齐,显示内容的中间部分 */
+ FIT_RESIZE_COVER_TOP_LEFT = 14, /* 保持动画终态内容的宽高比进行缩小或放大,使内容的两边都恰好大于或等于组件两边。当内容宽方向有剩余时,内容与组件保持左侧对齐,显示内容的左侧部分。当内容高方向有剩余时,内容与组件保持顶部对齐,显示内容的顶侧部分 */
+ FIT_RESIZE_COVER_BOTTOM_RIGHT = 15 /* 保持动画终态内容的宽高比进行缩小或放大,使内容的两边都恰好大于或等于组件两边。当内容宽方向有剩余时,内容与组件保持右侧对齐,显示内容的右侧部分。当内容高方向有剩余时,内容与组件保持底部对齐,显示内容的底侧部分 */
+ };
+ inline static constexpr const char* NativeNodeFitIdentifier = "nativenoderenderfit";
+ virtual void setNativeNodeRenderFit(QOpenHarmonyWindow::NativeNodeRenderFit fit) = 0;
+};
+#endif
+
} // QNativeInterface::Private
QT_END_NAMESPACE
@@ -1899,6 +1899,9 @@ void QWindow::setFramePosition(const QPoint &point)
*/
void QWindow::setPosition(const QPoint &pt)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWindow::setPosition");
+#endif
setGeometry(QRect(pt, size()));
}
@@ -1952,6 +1955,9 @@ void QWindow::resize(int w, int h)
*/
void QWindow::resize(const QSize &newSize)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWindow::resize (%dx%d)", newSize.width(), newSize.height());
+#endif
Q_D(QWindow);
d->positionPolicy = QWindowPrivate::WindowFrameExclusive;
if (d->platformWindow) {
@@ -2999,8 +3005,10 @@ void QWindowPrivate::setCursor(const QCursor *newCursor)
cursor = *newCursor;
hasCursor = true;
} else {
+#ifndef Q_OS_OPENHARMONY
if (!hasCursor)
return;
+#endif
cursor = QCursor(Qt::ArrowCursor);
hasCursor = false;
}
@@ -3057,6 +3065,10 @@ void *QWindow::resolveInterface(const char *name, int revision) const
QT_NATIVE_INTERFACE_RETURN_IF(QWaylandWindow, platformWindow);
#endif
+#if defined(Q_OS_OPENHARMONY)
+ QT_NATIVE_INTERFACE_RETURN_IF(QOpenHarmonyWindow, platformWindow);
+#endif
+
return nullptr;
}
@@ -276,6 +276,14 @@ QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination)
QWindowSystemInterfacePrivate::ApplicationTermination);
}
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+QT_DEFINE_QPA_EVENT_HANDLER(bool, handleApplicationTermination, std::function<void(bool)> resolve)
+{
+ return handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowSystemEvent, Delivery>(
+ QWindowSystemInterfacePrivate::ApplicationTermination, resolve);
+}
+#endif
+
QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry)
: WindowSystemEvent(GeometryChange)
, window(window)
@@ -345,6 +353,14 @@ QT_DEFINE_QPA_EVENT_HANDLER(bool, handleCloseEvent, QWindow *window)
return handleWindowSystemEvent<QWindowSystemInterfacePrivate::CloseEvent, Delivery>(window);
}
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+QT_DEFINE_QPA_EVENT_HANDLER(bool, handleCloseEvent, QWindow *window, QCloseEvent::CloseReason reason, std::function<void(bool)> resolve)
+{
+ Q_ASSERT(window);
+ return handleWindowSystemEvent<QWindowSystemInterfacePrivate::CloseEvent, Delivery>(window, reason, resolve);
+}
+#endif
+
/*!
\a w == 0 means that the event is in global coords only, \a local will be ignored in this case
@@ -176,6 +176,10 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static bool handleCloseEvent(QWindow *window);
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+ template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
+ static bool handleCloseEvent(QWindow *window, QCloseEvent::CloseReason reason, std::function<void(bool)> resolve);
+#endif
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleEnterEvent(QWindow *window, const QPointF &local = QPointF(), const QPointF& global = QPointF());
@@ -198,6 +202,10 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static bool handleApplicationTermination();
+#if defined(Q_OS_OPENHARMONY) && OHOS_SDK_VERSION >= 15
+ template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
+ static bool handleApplicationTermination(std::function<void(bool)> resolve);
+#endif
#if QT_CONFIG(draganddrop)
static QPlatformDragQtResponse handleDrag(QWindow *window, const QMimeData *dropData,
@@ -88,6 +88,15 @@ public:
EventType type;
int flags;
bool eventAccepted;
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ using Resolve = std::function<void(bool)>;
+ explicit WindowSystemEvent(EventType t, Resolve r)
+ : type(t), flags(0), eventAccepted(true), resolve(r) { }
+
+ Resolve resolve { nullptr };
+#endif
+#endif
};
class CloseEvent : public WindowSystemEvent {
@@ -96,6 +105,14 @@ public:
: WindowSystemEvent(Close), window(w)
{ }
QPointer<QWindow> window;
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ explicit CloseEvent(QWindow *w, QCloseEvent::CloseReason cr, Resolve r)
+ : WindowSystemEvent(Close, r), window(w), reason(cr)
+ { }
+ QCloseEvent::CloseReason reason = QCloseEvent::InternalClose;
+#endif
+#endif
};
class GeometryChangeEvent : public WindowSystemEvent {
@@ -115,6 +115,9 @@ QWindow* QBackingStore::window() const
void QBackingStore::beginPaint(const QRegion ®ion)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QBackingStore::beginPaint");
+#endif
const qreal dpr = d_ptr->backingStoreDevicePixelRatio();
if (d_ptr->highDpiBackingstore &&
@@ -154,6 +157,9 @@ void QBackingStore::beginPaint(const QRegion ®ion)
*/
QPaintDevice *QBackingStore::paintDevice()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QBackingStore::paintDevice");
+#endif
QPaintDevice *device = handle()->paintDevice();
if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image)
@@ -172,6 +178,9 @@ QPaintDevice *QBackingStore::paintDevice()
*/
void QBackingStore::endPaint()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QBackingStore::endPaint");
+#endif
if (paintDevice()->paintingActive())
qWarning("QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?");
@@ -194,6 +203,9 @@ void QBackingStore::endPaint()
*/
void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint &offset)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QBackingStore::flush");
+#endif
QWindow *topLevelWindow = this->window();
if (!window)
@@ -229,6 +241,9 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint &
*/
void QBackingStore::resize(const QSize &size)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QBackingStore::resize");
+#endif
d_ptr->size = size;
const qreal factor = d_ptr->deviceIndependentToNativeFactor();
handle()->resize(QHighDpi::scale(size, factor), QHighDpi::scale(d_ptr->staticContents, factor));
@@ -487,7 +487,16 @@ inline static void comp_func_Clear_template(typename Ops::Type *dest, int length
}
}
-void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+#ifndef OPENHARMONY_ALWAYS_INLINE
+ #ifdef Q_OS_OPENHARMONY
+ #define OPENHARMONY_ALWAYS_INLINE __attribute__((always_inline))
+ #else
+ #define OPENHARMONY_ALWAYS_INLINE
+ #endif
+#endif
+
+
+OPENHARMONY_ALWAYS_INLINE void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
{
comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
}
@@ -557,7 +566,7 @@ inline static void comp_func_Source_template(typename Ops::Type *Q_DECL_RESTRICT
}
}
-void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
+OPENHARMONY_ALWAYS_INLINE void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
{
comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha);
}
@@ -667,7 +676,7 @@ inline static void comp_func_SourceOver_template(typename Ops::Type *Q_DECL_REST
}
}
-void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
+OPENHARMONY_ALWAYS_INLINE void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
{
comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha);
}
@@ -12,7 +12,16 @@
QT_BEGIN_NAMESPACE
-void qt_memfill32(quint32 *dest, quint32 value, qsizetype count)
+#ifndef OPENHARMONY_ALWAYS_INLINE
+ #ifdef Q_OS_OPENHARMONY
+ #define OPENHARMONY_ALWAYS_INLINE __attribute__((always_inline))
+ #else
+ #define OPENHARMONY_ALWAYS_INLINE
+ #endif
+#endif
+
+
+OPENHARMONY_ALWAYS_INLINE void qt_memfill32(quint32 *dest, quint32 value, qsizetype count)
{
const int epilogueSize = count % 16;
#if defined(Q_CC_GHS) || defined(Q_CC_MSVC)
@@ -764,7 +773,7 @@ void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, int x, int
}
#endif
-void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha)
+OPENHARMONY_ALWAYS_INLINE void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha)
{
if ((const_alpha & qAlpha(color)) == 255) {
qt_memfill32(destPixels, color, length);
@@ -1286,7 +1295,7 @@ void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const Q
convertARGBToARGB32PM_neon<true>(buffer, buffer, count);
}
-const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
+OPENHARMONY_ALWAYS_INLINE const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
const QList<QRgb> *, QDitherInfo *)
{
convertARGBToARGB32PM_neon<false>(buffer, reinterpret_cast<const uint *>(src) + index, count);
@@ -1335,7 +1344,7 @@ void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int i
convertARGBFromARGB32PM_neon<false,true>(d, src, count);
}
-void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
+OPENHARMONY_ALWAYS_INLINE void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
const QList<QRgb> *, QDitherInfo *)
{
uint *d = reinterpret_cast<uint *>(dest) + index;
@@ -1362,6 +1362,9 @@ void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
static void fillRect_normalized(const QRect &r, QSpanData *data,
QRasterPaintEnginePrivate *pe)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("fillRect_normalized");
+#endif
int x1, x2, y1, y2;
bool rectClipped = true;
@@ -2095,6 +2098,9 @@ static inline const QRect toAlignedRect_positive(const QRectF &rect)
*/
void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QRasterPaintEngine::drawImage");
+#endif
#ifdef QT_DEBUG_DRAW
qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
#endif
new file mode 100644
@@ -0,0 +1,10 @@
+#include <qpa/qplatformwindow_p.h>
+#include <QtCore/private/qnativeinterface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QNativeInterface::Private;
+
+QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QOpenHarmonyWindow);
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,7 @@
+HEADERS += \
+ $$PWD/qohfontdatabase_p.h
+
+SOURCES += \
+ $$PWD/qohfontdatabase.cpp
+
+LIBS += -lnative_drawing
new file mode 100644
@@ -0,0 +1,313 @@
+#include "qohfontdatabase_p.h"
+#include <native_drawing/drawing_text_font_descriptor.h>
+#include <native_drawing/drawing_text_typography.h>
+#include <private/qfontengine_ft_p.h>
+#include <ft2build.h>
+#include <QtCore/QFile>
+#include FT_TRUETYPE_TABLES_H
+#include FT_ERRORS_H
+
+class QOhFontDatabasePrivate
+{
+ Q_DECLARE_PUBLIC(QOhFontDatabase)
+public:
+ QOhFontDatabasePrivate()
+ {
+ auto fontconfig = OH_Drawing_GetSystemFontConfigInfo(nullptr);
+ if (fontconfig) {
+ m_defaultFontFamily = QString::fromUtf8(fontconfig->fontGenericInfoSet->familyName);
+ m_fallbackFontFamily = QString::fromUtf8(fontconfig->fallbackGroupSet->fallbackInfoSet->familyName);
+ OH_Drawing_DestroySystemFontConfigInfo(fontconfig);
+ }
+ }
+
+ void initFontDatabaseByType(OH_Drawing_SystemFontType type);
+
+ ~QOhFontDatabasePrivate()
+ {
+
+ }
+ QFont defaultFont() const
+ {
+ return m_defaultFontFamily.isEmpty() ? QFont()
+ : QFont(m_defaultFontFamily, 12, QFont::Normal);
+ }
+
+private:
+ QOhFontDatabase *q_ptr;
+ mutable QHash<QPlatformTheme::Font, QFont> m_themeFonts;
+ QHash<OH_Drawing_SystemFontType, QStringList> m_systemFontFamilies;
+ QString m_defaultFontFamily;
+ QString m_fallbackFontFamily;
+};
+
+QOhFontDatabase::QOhFontDatabase()
+ : QGenericUnixFontDatabase(),
+ m_d(new QOhFontDatabasePrivate())
+{
+ m_d->q_ptr = this;
+}
+
+QOhFontDatabase::~QOhFontDatabase()
+{
+
+}
+
+QString QOhFontDatabase::fontDir() const
+{
+ QString dir = QGenericUnixFontDatabase::fontDir();
+ auto fontconfig = OH_Drawing_GetSystemFontConfigInfo(nullptr);
+ if (fontconfig) {
+ if (fontconfig->fontDirSize > 0) {
+ dir = QString::fromUtf8(fontconfig->fontDirSet[0]);
+ }
+ OH_Drawing_DestroySystemFontConfigInfo(fontconfig);
+ }
+ return dir;
+}
+
+void QOhFontDatabasePrivate::initFontDatabaseByType(OH_Drawing_SystemFontType type)
+{
+ std::unique_ptr<::OH_Drawing_Array, decltype(&::OH_Drawing_DestroySystemFontFullNames)> drawingArray(::OH_Drawing_GetSystemFontFullNamesByType(type),
+ ::OH_Drawing_DestroySystemFontFullNames);
+
+ qsizetype size = ::OH_Drawing_GetDrawingArraySize(drawingArray.get());
+ for (qsizetype i = 0; i < size; i++) {
+ auto drawingString = ::OH_Drawing_GetSystemFontFullNameByIndex(drawingArray.get(), i);
+ std::unique_ptr<::OH_Drawing_FontDescriptor, decltype(&::OH_Drawing_DestroyFontDescriptor)> descriptor(::OH_Drawing_GetFontDescriptorByFullName(drawingString, (::OH_Drawing_SystemFontType)type),
+ ::OH_Drawing_DestroyFontDescriptor);
+ if (descriptor)
+ {
+ m_systemFontFamilies[type] << QOhFontDatabase::addFontFile(QByteArray(), descriptor->path);
+ }
+ }
+}
+
+bool isTTCFile(const QByteArray &fontData, const QByteArray &filePath) {
+ QByteArray header;
+
+ if (!fontData.isEmpty()) {
+ if (fontData.size() >= 4) {
+ header = fontData.left(4);
+ }
+ } else {
+ QFile file(QString::fromUtf8(filePath));
+ if (file.open(QIODevice::ReadOnly) && file.size() >= 4) {
+ header = file.read(4);
+ file.close();
+ }
+ }
+
+ if (header == QByteArray("ttcf", 4)) {
+ return true;
+ }
+ return false;
+}
+
+extern FT_Library qt_getFreetype();
+QStringList QOhFontDatabase::addFontFile(const QByteArray &fontData, const QByteArray &file)
+{
+ FT_Library library = qt_getFreetype();
+ int index = 0;
+ int numFaces = 0;
+ QStringList families;
+
+ bool isTTC = isTTCFile(fontData, file);
+ do {
+ FT_Face face;
+ FT_Error error;
+ int faceIndex = index;
+ if (!isTTC && index > 0)
+ faceIndex = index << 16;
+ if (!fontData.isEmpty()) {
+ error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), faceIndex, &face);
+ } else {
+ error = FT_New_Face(library, file.constData(), faceIndex, &face);
+ }
+
+ if (error != FT_Err_Ok) {
+ qDebug() << "FT_New_Face failed with index" << index << "(faceIndex:" << faceIndex << "):" << Qt::hex << error;
+ break;
+ }
+
+ if (faceIndex == 0) {
+ if (isTTC) {
+ numFaces = face->num_faces;
+ } else {
+ numFaces = (face->style_flags >> 16) & 0xFFFF;
+ numFaces += 1;
+ }
+ }
+
+ QFont::Weight weight = QFont::Normal;
+ QFont::Style style = QFont::StyleNormal;
+
+ if (face->style_flags & FT_STYLE_FLAG_ITALIC)
+ style = QFont::StyleItalic;
+ if (face->style_flags & FT_STYLE_FLAG_BOLD)
+ weight = QFont::Bold;
+
+ bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
+ QSupportedWritingSystems writingSystems;
+
+ // detect symbol fonts
+ for (int i = 0; i < face->num_charmaps; ++i) {
+ FT_CharMap cm = face->charmaps[i];
+ if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM || cm->encoding == FT_ENCODING_MS_SYMBOL) {
+ writingSystems.setSupported(QFontDatabase::Symbol);
+ break;
+ }
+ }
+
+ QFont::Stretch stretch = QFont::Unstretched;
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+ if (os2) {
+ quint32 unicodeRange[4] = {
+ quint32(os2->ulUnicodeRange1),
+ quint32(os2->ulUnicodeRange2),
+ quint32(os2->ulUnicodeRange3),
+ quint32(os2->ulUnicodeRange4)
+ };
+ quint32 codePageRange[2] = {
+ quint32(os2->ulCodePageRange1),
+ quint32(os2->ulCodePageRange2)
+ };
+
+ writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
+
+ if (os2->usWeightClass) {
+ weight = static_cast<QFont::Weight>(os2->usWeightClass);
+ } else if (os2->panose[2]) {
+ int w = os2->panose[2];
+ if (w <= 1)
+ weight = QFont::Thin;
+ else if (w <= 2)
+ weight = QFont::ExtraLight;
+ else if (w <= 3)
+ weight = QFont::Light;
+ else if (w <= 5)
+ weight = QFont::Normal;
+ else if (w <= 6)
+ weight = QFont::Medium;
+ else if (w <= 7)
+ weight = QFont::DemiBold;
+ else if (w <= 8)
+ weight = QFont::Bold;
+ else if (w <= 9)
+ weight = QFont::ExtraBold;
+ else if (w <= 10)
+ weight = QFont::Black;
+ }
+
+ switch (os2->usWidthClass) {
+ case 1:
+ stretch = QFont::UltraCondensed;
+ break;
+ case 2:
+ stretch = QFont::ExtraCondensed;
+ break;
+ case 3:
+ stretch = QFont::Condensed;
+ break;
+ case 4:
+ stretch = QFont::SemiCondensed;
+ break;
+ case 5:
+ stretch = QFont::Unstretched;
+ break;
+ case 6:
+ stretch = QFont::SemiExpanded;
+ break;
+ case 7:
+ stretch = QFont::Expanded;
+ break;
+ case 8:
+ stretch = QFont::ExtraExpanded;
+ break;
+ case 9:
+ stretch = QFont::UltraExpanded;
+ break;
+ }
+ }
+
+ QString family = QString::fromUtf8(face->family_name);
+ QString styleName = QString::fromUtf8(face->style_name);
+
+ FontFile *fontFile = new FontFile;
+ fontFile->fileName = QFile::decodeName(file);
+ fontFile->indexValue = faceIndex;
+ registerFont(family, styleName, QString(), weight, style, stretch,
+ true, true, 0, fixedPitch, writingSystems, fontFile);
+
+
+ families.append(family);
+
+ FT_Done_Face(face);
+ ++index;
+ } while (index < numFaces);
+
+ return families;
+}
+
+void QOhFontDatabase::populateFontDatabase()
+{
+ Q_D(QOhFontDatabase);
+ d->initFontDatabaseByType(OH_Drawing_SystemFontType::GENERIC);
+ d->initFontDatabaseByType(OH_Drawing_SystemFontType::STYLISH);
+ d->initFontDatabaseByType(OH_Drawing_SystemFontType::INSTALLED);
+}
+
+QStringList QOhFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
+{
+ QStringList result;
+ /*
+ * 从系统中获取通用字体
+ * 当前为 HarmonyOS Sans 字体
+ * 仅支持英文
+ * @since 14
+ */
+ Q_D(const QOhFontDatabase);
+ if (d->m_defaultFontFamily.isEmpty() && d->m_fallbackFontFamily.isEmpty()
+ && d->m_systemFontFamilies[OH_Drawing_SystemFontType::GENERIC].isEmpty())
+ return QGenericUnixFontDatabase::fallbacksForFamily(family, style, styleHint, script);
+ result << d->m_defaultFontFamily;
+ /*
+ * 从系统中获取备选字体
+ * 只有 HMOS Color Emoji 字体
+ * 仅支持英文
+ * @since 14
+ */
+ result << d->m_fallbackFontFamily;
+ /*
+ * 用系统字体作为备选字体,
+ * 风格字体与默认字体的风格可能存在较大差异,
+ * 不考虑风格字体作为备选字体
+ */
+ result << d->m_systemFontFamilies[OH_Drawing_SystemFontType::GENERIC];
+ return result;
+}
+
+QFont QOhFontDatabase::defaultFont() const
+{
+ Q_D(const QOhFontDatabase);
+ return d->defaultFont();
+}
+
+QFont QOhFontDatabase::themeFont(QPlatformTheme::Font) const
+{
+ /* Todo 根据类型创建字体 */
+ Q_D(const QOhFontDatabase);
+ return d->defaultFont();
+}
+
+const QHash<QPlatformTheme::Font, QFont> &QOhFontDatabase::themeFonts() const
+{
+ Q_D(const QOhFontDatabase);
+ if (d->m_themeFonts.isEmpty()) {
+ for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) {
+ QPlatformTheme::Font ft = static_cast<QPlatformTheme::Font>(f);
+ d->m_themeFonts.insert(ft, themeFont(ft));
+ }
+ }
+ return d->m_themeFonts;
+}
new file mode 100644
@@ -0,0 +1,27 @@
+#ifndef QOHFONTDATABASE_P_H
+#define QOHFONTDATABASE_P_H
+#include <private/qgenericunixfontdatabase_p.h>
+#include <qpa/qplatformtheme.h>
+#include <QScopedPointer>
+#include <QStringList>
+class QOhFontDatabasePrivate;
+class Q_GUI_EXPORT QOhFontDatabase: public QGenericUnixFontDatabase
+{
+ Q_DECLARE_PRIVATE_D(m_d, QOhFontDatabase)
+public:
+ QOhFontDatabase();
+ ~QOhFontDatabase();
+ void populateFontDatabase() override;
+ QString fontDir() const override;
+ QFont defaultFont() const override;
+ QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
+
+ // For OpenHarmony platform themes
+ QFont themeFont(QPlatformTheme::Font) const;
+ const QHash<QPlatformTheme::Font, QFont> &themeFonts() const;
+ static QStringList addFontFile(const QByteArray &fontData, const QByteArray &file);
+private:
+ QScopedPointer<QOhFontDatabasePrivate> m_d;
+};
+
+#endif // QOHFONTDATABASE_P_H
@@ -1820,7 +1820,16 @@ QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, q
_text = _text.mid(posA);
}
QStackTextEngine engine(_text, QFont(d.data()));
- return engine.elidedText(mode, QFixed::fromReal(width), flags);
+/*The default font of HarmonyOS allows for adjusting the spacing of ellipses*/
+#ifdef Q_OS_OPENHARMONY
+ QString str = engine.elidedText(mode, QFixed::fromReal(width), flags);
+ while (str.length() > 1 && boundingRect(str).width() > width) {
+ str.remove(str.length() - 2, 1);
+ }
+ return str;
+#else
+ return engine.elidedText(mode, QFixed::fromReal(width), flags);
+#endif
}
/*!
@@ -205,6 +205,32 @@ bool QDesktopServices::openUrl(const QUrl &url)
return platformServices->openUrl(url);
}
+#ifdef Q_OS_OPENHARMONY
+bool QDesktopServices::openUri(const QUrl &url, bool isDir)
+{
+ if (!url.isValid())
+ return false;
+
+ QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ if (Q_UNLIKELY(!platformIntegration)) {
+ QCoreApplication *application = QCoreApplication::instance();
+ if (Q_UNLIKELY(!application))
+ qWarning("QDesktopServices::openUrl: Please instantiate the QGuiApplication object "
+ "first");
+ else if (Q_UNLIKELY(!qobject_cast<QGuiApplication *>(application)))
+ qWarning("QDesktopServices::openUrl: Application is not a GUI application");
+ return false;
+ }
+
+ QPlatformServices *platformServices = platformIntegration->services();
+ if (!platformServices) {
+ qWarning("The platform plugin does not support services.");
+ return false;
+ }
+ return platformServices->openUri(url, isDir);
+}
+#endif
+
/*!
Sets the handler for the given \a scheme to be the handler \a method provided by
the \a receiver object.
@@ -19,6 +19,9 @@ class Q_GUI_EXPORT QDesktopServices
{
public:
static bool openUrl(const QUrl &url);
+#ifdef Q_OS_OPENHARMONY
+ static bool openUri(const QUrl &url, bool isDir = false);
+#endif
static void setUrlHandler(const QString &scheme, QObject *receiver, const char *method);
static void unsetUrlHandler(const QString &scheme);
};
@@ -200,6 +200,11 @@ qt_internal_extend_target(Network CONDITION ANDROID AND QT_FEATURE_dnslookup
kernel/qdnslookup_android.cpp
)
+qt_internal_extend_target(Network CONDITION OHOS AND QT_FEATURE_dnslookup
+ SOURCES
+ kernel/qdnslookup_openharmony.cpp
+)
+
qt_internal_extend_target(Network CONDITION WIN32
SOURCES
kernel/qhostinfo_win.cpp
@@ -305,7 +310,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver
socket/qlocalsocket.cpp socket/qlocalsocket.h socket/qlocalsocket_p.h
)
-qt_internal_extend_target(Network CONDITION INTEGRITY AND QT_FEATURE_localserver
+qt_internal_extend_target(Network CONDITION INTEGRITY AND OHOS AND QT_FEATURE_localserver
SOURCES
socket/qlocalserver_tcp.cpp
socket/qlocalsocket_tcp.cpp
@@ -354,7 +359,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ocsp AND QT_FEATURE_opens
ssl/qocsp_p.h
)
-qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND UNIX AND NOT ANDROID
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND UNIX AND NOT ANDROID AND NOT OHOS
SOURCES
kernel/qdnslookup_unix.cpp
)
@@ -20,6 +20,9 @@ QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const
<< QStringLiteral("qrc");
#if defined(Q_OS_ANDROID)
schemes << QStringLiteral("assets");
+#endif
+#if defined(Q_OS_OPENHARMONY)
+ schemes << QStringLiteral("rawfile");
#endif
return schemes;
}
@@ -43,6 +46,9 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
if (url.scheme().compare("qrc"_L1, Qt::CaseInsensitive) == 0
#if defined(Q_OS_ANDROID)
|| url.scheme().compare("assets"_L1, Qt::CaseInsensitive) == 0
+#endif
+#if defined(Q_OS_OPENHARMONY)
+ || url.scheme().compare("rawfile"_L1, Qt::CaseInsensitive) == 0
#endif
|| url.isLocalFile()) {
return new QNetworkAccessFileBackend;
@@ -103,6 +109,11 @@ void QNetworkAccessFileBackend::open()
if (url.scheme() == "assets"_L1)
fileName = "assets:"_L1 + url.path();
else
+#endif
+#if defined(Q_OS_OPENHARMONY)
+ if (url.scheme() == "rawfile"_L1)
+ fileName = "rawfile"_L1 + url.path();
+ else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
}
@@ -1152,6 +1152,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (isLocalFile
#ifdef Q_OS_ANDROID
|| scheme == "assets"_L1
+#endif
+#ifdef Q_OS_OPENHARMONY
+ || scheme == "rawfile"_L1
#endif
|| scheme == "qrc"_L1) {
return new QNetworkReplyFileImpl(this, req, op);
@@ -78,6 +78,11 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
if (scheme == "assets"_L1)
fileName = "assets:"_L1 + url.path();
else
+#endif
+#if defined(Q_OS_OPENHARMONY)
+ if (scheme == "rawfile"_L1)
+ fileName = "rawfile:"_L1 + url.path();
+ else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
}
new file mode 100644
@@ -0,0 +1,46 @@
+#include "qdnslookup_p.h"
+
+#include "qjsmodule.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName,
+ const QHostAddress &nameserver, QDnsLookupReply *reply)
+{
+ Q_UNUSED(requestType);
+ Q_UNUSED(nameserver);
+
+ QtOh::runOnJsUIThreadWithPromise<bool>([requestName, &reply](auto p) {
+ QJsModule jsNetMoudle("@ohos.net.connection");
+ QJsPromise promise(jsNetMoudle.call(
+ "getAddressesByName", { Napi::String::New(QtOh::uiEnv(), requestName.data()) }).As<Napi::Promise>());
+ promise.onThen([p, requestName, &reply](const Napi::CallbackInfo &info) {
+ Napi::Object netAddress = info[0].As<Napi::Object>();
+ QString address = QString::fromStdString(netAddress.Get("address").ToString());
+ int family = netAddress.Get("family").ToNumber().Int32Value();
+ int port = netAddress.Get("port").ToNumber().Int32Value();
+ qWarning() << "getAddressesByName success:" << address << family << port << requestName.data();
+ QDnsHostAddressRecord record;
+ record.d->value = QHostAddress(address);
+ reply->hostAddressRecords.append(record);
+ p->set_value(true);
+ })
+ .onCatch([p, requestName, &reply](const Napi::CallbackInfo &info) {
+ Napi::Object error = info[0].As<Napi::Object>();
+ Napi::Number code = error.Get("code").ToNumber();
+ Napi::String message = error.Get("message").ToString();
+ qWarning() << "getAddressesByName fail:" << code.Int32Value()
+ << QString::fromStdString(message) << requestName.data();
+ reply->error = QDnsLookup::ServerFailureError;
+ reply->errorString = tr("Server failure");
+ p->set_value(false);
+ return;
+ });
+ });
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,17 @@
+#####################################################################
+## OpenHarmony Template Files:
+#####################################################################
+
+
+set(openharmony_template_dirs
+ abilitystage
+ entryability
+ pages
+)
+
+foreach(dir IN LISTS openharmony_template_dirs)
+ qt_copy_or_install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/
+ DESTINATION openharmony/qtbase/${dir}/
+ FILES_MATCHING PATTERN "*.ts" PATTERN "*.ets"
+ )
+endforeach()
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,8 @@
+import { AbilityStage } from '@kit.AbilityKit';
+import qpa from 'libplugins_platforms_qopenharmony.so'
+
+export default class MyAbilityStage extends AbilityStage {
+ onCreate(): void {
+ qpa.attachAbilityStage(this);
+ }
+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,64 @@
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+import UIAbility from '@ohos.app.ability.UIAbility';
+import Window from '@ohos.window'
+import Want from '@ohos.app.ability.Want';
+import qpa from 'libplugins_platforms_qopenharmony.so'
+import hilog from '@ohos.hilog';
+
+
+export default class EntryAbility extends UIAbility {
+ tag = "QtForOpenHarmony";
+ domain = 0xFF00;
+ private launchApplication = "libentry.so"
+ private launchParams = ""
+
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+ super.onCreate(want, launchParam)
+ }
+
+ newLocalStorage() {
+ return new LocalStorage()
+ }
+
+ onDestroy() {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: Window.WindowStage) {
+ // Main window is created, set main page for this ability
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onWindowStageCreate');
+ this.launchParamSet();
+ qpa.startQtApplication(this);
+ }
+
+ async launchParamSet(){
+ let params:string[] = [];
+
+ //NOTE:应用启动参数在此处使用params.push添加
+ this.launchParams = params.join(' ');
+ }
+
+ onWindowStageDestroy() {
+ // Main window is destroyed, release UI related resources
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onWindowStageDestroy');
+ }
+
+ onForeground() {
+ // Ability has brought to foreground
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground() {
+ // Ability has back to background
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onBackground');
+ }
+
+ /**
+ * note:启动模式为singleton/specified才生效
+ * @param want
+ * @param launchParam
+ */
+ onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+ qpa.handleOnNewWant(want, launchParam);
+ }
+};
new file mode 100644
@@ -0,0 +1,51 @@
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { UIExtensionContentSession, Want } from '@kit.AbilityKit';
+import EmbeddedUIExtensionAbility from '@ohos.app.ability.EmbeddedUIExtensionAbility';
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+import qpa from 'libplugins_platforms_qopenharmony.so'
+
+export default class EntryEmbeddedAbility extends EmbeddedUIExtensionAbility {
+ tag = "QtForOpenHarmony";
+ domain = 0x0000;
+ private name: string = "qt_entry_window"
+ private launchParams = ""
+ private launchApplication = "libentry.so"
+ private launchWant: Want = {}
+ onCreate(launchParam: AbilityConstant.LaunchParam) {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onCreate');
+ }
+
+ onForeground() {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground() {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onBackground');
+ }
+
+ onDestroy() {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onDestroy');
+ }
+
+ onSessionCreate(want: Want, session: UIExtensionContentSession) {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onSessionCreate');
+ this.launchWant = want;
+ this.launchParamSet();
+ qpa.startQtApplication(this, session);
+ }
+
+ newLocalStorage() {
+ return new LocalStorage();
+ }
+
+ async launchParamSet(){
+ let params:string[] = [];
+
+ //NOTE:应用启动参数在此处添加
+ this.launchParams = params.join(' ');
+ }
+ onSessionDestroy(session: UIExtensionContentSession) {
+ hilog.info(this.domain, this.tag, '%{public}s', 'Ability onSessionDestroy');
+ }
+};
+
new file mode 100644
@@ -0,0 +1,18 @@
+let storage = LocalStorage.getShared()
+
+@Entry(storage)
+@Component
+struct Index {
+ @LocalStorageProp("idName") windowId: string = "";
+
+ build() {
+ Stack({ alignContent: Alignment.TopStart }) {
+ XComponent({
+ id: this.windowId,
+ type: XComponentType.NODE,
+ libraryname: 'plugins_platforms_qopenharmony'
+ }).width('100%')
+ .height('100%')
+ }
+ }
+}
@@ -1,6 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(OHOS)
+ add_subdirectory(openharmony)
+endif()
if(ANDROID)
add_subdirectory(android)
endif()
new file mode 100644
@@ -0,0 +1,224 @@
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QOhIntegrationPlugin Plugin:
+#####################################################################
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(JSCLASS_SOURCES
+ jsclass/qjsability.h
+ jsclass/qjsabilitystage.h
+ jsclass/qjsaccessmanager.h
+ jsclass/qjsdisplay.h
+ jsclass/qjsscreen.h
+ jsclass/qjsdeviceinfo.h
+ jsclass/qjsuiability.h
+ jsclass/qjsbasecontext.h
+ jsclass/qjscontext.h
+ jsclass/qjsapplicationcontext.h
+ jsclass/qjsuiabilitycontext.h
+ jsclass/qjsuiextensionability.h
+ jsclass/qjsuiextensioncontext.h
+ jsclass/qjsuiextensioncontextsession.h
+ jsclass/qjswant.h
+ jsclass/qjswindowproxy.h
+ jsclass/qjswindowstage.h
+ jsclass/qjscursor.h
+ jsclass/qjssettings.h
+ jsclass/qjsabilityinfo.h
+ jsclass/qjsabilityfactory.h
+ jsclass/qjsability.cpp
+ jsclass/qjsabilitystage.cpp
+ jsclass/qjsaccessmanager.cpp
+ jsclass/qjsdisplay.cpp
+ jsclass/qjsscreen.cpp
+ jsclass/qjsdeviceinfo.cpp
+ jsclass/qjsuiability.cpp
+ jsclass/qjsbasecontext.cpp
+ jsclass/qjscontext.cpp
+ jsclass/qjsapplicationcontext.cpp
+ jsclass/qjsuiabilitycontext.cpp
+ jsclass/qjsuiextensionability.cpp
+ jsclass/qjsuiextensioncontext.cpp
+ jsclass/qjsuiextensioncontextsession.cpp
+ jsclass/qjswant.cpp
+ jsclass/qjswindowproxy.cpp
+ jsclass/qjswindowstage.cpp
+ jsclass/qjscursor.cpp
+ jsclass/qjssettings.cpp
+ jsclass/qjsabilityinfo.cpp
+ jsclass/qjsabilityfactory.cpp
+)
+
+set(INPUTMETHOD_SOURCES
+ inputmethod/qohcontroller.h
+ inputmethod/qohtexteditorproxy.h
+ inputmethod/qohinputmethodproxy.h
+ inputmethod/qohinputmethod.h
+ inputmethod/qohattachoptions.h
+ inputmethod/qohcursorinfo.h
+ inputmethod/qohtextconfig.h
+ inputmethod/qohtextavoidinfo.h
+ inputmethod/qohcontroller.cpp
+ inputmethod/qohtexteditorproxy.cpp
+ inputmethod/qohinputmethodproxy.cpp
+ inputmethod/qohinputmethod.cpp
+ inputmethod/qohattachoptions.cpp
+ inputmethod/qohcursorinfo.cpp
+ inputmethod/qohtextconfig.cpp
+ inputmethod/qohtextavoidinfo.cpp
+)
+
+qt_internal_add_plugin(QOhIntegrationPlugin
+ OUTPUT_NAME qopenharmony
+ PLUGIN_TYPE platforms
+ DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES openharmony
+ SOURCES
+ qohplatformintegration.h
+ qohplatformwindow.h
+ qohplatformopenglwindow.h
+ qohplatformforeignwindow.h
+ qohplatformscreen.h
+ qohplatformbackingstore.h
+ qohxcomponent.h
+ qohnativewindowmanager.h
+ qohplatformopenglcontext.h
+ qoheventdispatcher.h
+ qohplatformoffscreensurface.h
+ qohmain.h
+ qohplatforminputcontext.h
+ qohplatformtheme.h
+ qohplatformdialoghelpers.h
+ qohfileenginehandler.h
+ qohnativewindow.h
+ qohjsonlistener.h
+ qohwindownode.h
+ qohwindownodeeventhandler.h
+ qohplatformservices.h
+ qohplatformcursor.h
+ qohplatformabilityctrl.h
+ qohplatformnativeinterface.h
+ qohevent.h
+ qohextensionwindow.h
+ qohnormalwindow.h
+ qohauxiliary.h
+ qohplatformmenu.h
+ qohobjectholder.h
+ qohnativenodeapi.h
+ qoharkui.h
+ qohwindowcontext.h
+ qohkeys.h
+ qohdesktopwindow.h
+ qohplatformplugin.cpp
+ qohplatformwindow.cpp
+ qohplatformintegration.cpp
+ qohplatformopenglwindow.cpp
+ qohplatformforeignwindow.cpp
+ qohplatformscreen.cpp
+ qohplatformbackingstore.cpp
+ qohplatformopenglcontext.cpp
+ qohplatformoffscreensurface.cpp
+ qohmain.cpp
+ qohxcomponent.cpp
+ qohnativewindowmanager.cpp
+ qoheventdispatcher.cpp
+ qohplatforminputcontext.cpp
+ qohplatformtheme.cpp
+ qohplatformdialoghelpers.cpp
+ qohfileenginehandler.cpp
+ qohnativewindow.cpp
+ qohjsonlistener.cpp
+ qohwindownode.cpp
+ qohwindownodeeventhandler.cpp
+ qohplatformservices.cpp
+ qohplatformcursor.cpp
+ qohplatformabilityctrl.cpp
+ qohplatformnativeinterface.cpp
+ qohextensionwindow.cpp
+ qohnormalwindow.cpp
+ qohauxiliary.cpp
+ qohplatformmenu.cpp
+ qohnativenodeapi.cpp
+ qoharkui.cpp
+ qohwindowcontext.cpp
+ qohkeys.cpp
+ qohdesktopwindow.cpp
+ arkui/qohdragevent.h
+ arkui/qohdragevent.cpp
+ ${INPUTMETHOD_SOURCES}
+ ${JSCLASS_SOURCES}
+ NO_UNITY_BUILD_SOURCES
+ INCLUDE_DIRECTORIES
+ jsclass
+ inputmethod
+ arkui
+ DEFINES
+ OHOS_PLATFORM
+ LIBRARIES
+ EGL::EGL
+ Qt::Core
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ ace_napi.z ohfileuri ace_ndk.z rawfile.z native_window
+ udmf pixelmap pasteboard ohinput native_display_manager qos hitrace_ndk.z
+ native_drawing native_window_manager ohinputmethod native_vsync image_source
+ ohresmgr ohinputmethod
+)
+
+qt_internal_extend_target(QOhIntegrationPlugin CONDITION QT_FEATURE_vulkan
+ SOURCES
+ qohplatformvulkaninstance.h
+ qohplatformvulkaninstance.cpp
+)
+
+qt_internal_extend_target(QOhIntegrationPlugin CONDITION QT_FEATURE_systemtrayicon
+ SOURCES
+ qohsystemtrayicon.cpp qohsystemtrayicon.h
+)
+
+qt_internal_extend_target(QOhIntegrationPlugin CONDITION QT_FEATURE_clipboard
+ SOURCES
+ qohplatformclipboard.cpp qohplatformclipboard.h
+)
+
+qt_internal_extend_target(QOhIntegrationPlugin CONDITION QT_FEATURE_clipboard AND QT_FEATURE_draganddrop
+ SOURCES
+ qohdrag.cpp qohdrag.h
+)
+
+if(${OHOS_SDK_VERSION} GREATER 13)
+ qt_internal_extend_target(QOhIntegrationPlugin CONDITION QT_FEATURE_accessibility
+ SOURCES
+ accessibility/qohaccessibility.h
+ accessibility/qohplatformaccessibility.h
+ accessibility/qohaccessibilityprovider.h
+ accessibility/qohaccessiblenode.h
+ accessibility/qohaccessibility.cpp
+ accessibility/qohplatformaccessibility.cpp
+ accessibility/qohaccessibilityprovider.cpp
+ accessibility/qohaccessiblenode.cpp
+ INCLUDE_DIRECTORIES
+ accessibility
+ )
+ qt_internal_extend_target(QOhIntegrationPlugin
+ SOURCES
+ qohdisplay.h
+ qohdisplaymanager.h
+ qohdisplaylistener.h
+ qohdisplay.cpp
+ qohdisplaymanager.cpp
+ qohdisplaylistener.cpp
+ )
+endif()
+
+if(${OHOS_SDK_VERSION} GREATER 14)
+ qt_internal_extend_target(QOhIntegrationPlugin
+ SOURCES
+ qohnativevsync.h
+ qohnativevsync.cpp
+ )
+endif()
+
new file mode 100644
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qohaccessibility.h \
+ $$PWD/qohplatformaccessibility.h \
+ $$PWD/qohaccessibilityprovider.h \
+ $$PWD/qohaccessiblenode.h
+
+SOURCES += \
+ $$PWD/qohaccessibility.cpp \
+ $$PWD/qohplatformaccessibility.cpp \
+ $$PWD/qohaccessibilityprovider.cpp \
+ $$PWD/qohaccessiblenode.cpp
new file mode 100644
@@ -0,0 +1,299 @@
+#include "qohaccessibility.h"
+#include "qohaccessiblenode.h"
+#include "qohplatformwindow.h"
+#include "qohwindowcontext.h"
+#include "qohauxiliary.h"
+#include "qohaccessibilityprovider.h"
+#include <QtGui/qwindow.h>
+#include <QtGui/qguiapplication.h>
+#include <qpa/qplatformintegration.h>
+#include <QtCore/qopenharmonydefines.h>
+#include <qpa/qplatformaccessibility.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <private/qaccessiblebridgeutils_p.h>
+#include <mutex>
+#include <string>
+#if QT_CONFIG(accessibility)
+
+QT_BEGIN_NAMESPACE
+namespace QtOhAccessibility {
+
+// arkUi框架设计的特殊值,根节点必须设置parentId为这个值
+const int parentOfRoot = -2100000;
+void fillAccessibilityElement(ArkUI_AccessibilityElementInfo *element, QOhAccessibleNode *node)
+{
+ QtOh::runOnQtMainThreadAndWait([node]{
+ node->update();
+ });
+ OH_ArkUI_AccessibilityElementInfoSetElementId(element, node->id());
+ OH_ArkUI_AccessibilityElementInfoSetParentId(element, node->parent() == nullptr ? parentOfRoot : node->parent()->id());
+ OH_ArkUI_AccessibilityElementInfoSetComponentType(element, node->type().toUtf8().constData());
+ OH_ArkUI_AccessibilityElementInfoSetContents(element, node->contents().isEmpty() ? node->name().toUtf8().constData() : node->contents().toUtf8().constData());
+ OH_ArkUI_AccessibilityElementInfoSetAccessibilityText(element, node->contents().toUtf8().constData());
+ OH_ArkUI_AccessibilityElementInfoSetHintText(element, node->hint().toUtf8().constData());
+ OH_ArkUI_AccessibilityElementInfoSetAccessibilityDescription(element, node->description().toUtf8().constData());
+ auto actions = node->actions();
+ OH_ArkUI_AccessibilityElementInfoSetOperationActions(element, actions.size(), actions.data());
+ QAccessible::State s = node->state();
+ OH_ArkUI_AccessibilityElementInfoSetVisible(element, node->isVisible());
+ OH_ArkUI_AccessibilityElementInfoSetEnabled(element, s.disabled != 1);
+ OH_ArkUI_AccessibilityElementInfoSetFocusable(element, node->focusable == 1);
+ OH_ArkUI_AccessibilityElementInfoSetFocused(element, s.focused == 1);
+ OH_ArkUI_AccessibilityElementInfoSetCheckable(element, s.checkable == 1);
+ OH_ArkUI_AccessibilityElementInfoSetChecked(element, s.checked == 1);
+ OH_ArkUI_AccessibilityElementInfoSetSelected(element, s.selected == 1);
+ OH_ArkUI_AccessibilityElementInfoSetIsPassword(element, s.passwordEdit == 1);
+ OH_ArkUI_AccessibilityElementInfoSetEditable(element, s.editable == 1);
+ OH_ArkUI_AccessibilityElementInfoSetClickable(element, node->clickable);
+ OH_ArkUI_AccessibilityElementInfoSetScrollable(element, node->scrollable);
+ QColor clr = node->backgroundColor();
+ QString name = clr.name();
+ QByteArray nameArray = name.toUtf8();
+ OH_ArkUI_AccessibilityElementInfoSetBackgroundColor(element, nameArray.constData());
+ auto valueInfo = node->valueInfo();
+ if (valueInfo.has_value()) {
+ ArkUI_AccessibleRangeInfo info = valueInfo.value();
+ OH_ArkUI_AccessibilityElementInfoSetRangeInfo(element, &info);
+ }
+ auto gridInfo = node->gridInfo();
+ if (gridInfo.has_value()) {
+ ArkUI_AccessibleGridInfo info = gridInfo.value();
+ OH_ArkUI_AccessibilityElementInfoSetGridInfo(element, &info);
+ }
+ auto gridItemInfo = node->gridItemInfo();
+ for (size_t i = 0; i < gridItemInfo.size(); ++i) {
+ auto item = gridItemInfo[i];
+ OH_ArkUI_AccessibilityElementInfoSetGridItemInfo(element, &item);
+ }
+ auto selections = node->selections();
+ if (!selections.empty()) {
+ auto first = selections.front();
+ OH_ArkUI_AccessibilityElementInfoSetSelectedTextStart(element, first.first);
+ OH_ArkUI_AccessibilityElementInfoSetSelectedTextEnd(element, first.second);
+ }
+
+ ArkUI_AccessibleRect rect = node->geometry();
+ OH_ArkUI_AccessibilityElementInfoSetScreenRect(element, &rect);
+}
+
+void fillChildrenAccessibilityElement(ArkUI_AccessibilityElementInfo *parentNode, ArkUI_AccessibilityElementInfoList *elementList, QOhAccessibleNode *node, bool recursive)
+{
+ if (!node->isVisible()) {
+ return;
+ }
+ auto c = node->children();
+ int64_t count = c.count();
+ if (count < 1)
+ return;
+ std::vector<int64_t> ids;
+ for (int64_t i = 0; i < count; ++i) {
+ ids.push_back(c.at(i)->id());
+ }
+ OH_ArkUI_AccessibilityElementInfoSetChildNodeIds(parentNode, ids.size(), &ids[0]);
+
+ std::vector<std::pair<ArkUI_AccessibilityElementInfo *, QOhAccessibleNode *>> children(count);
+ for (int i = 0; i < count; ++i) {
+ auto childNode = c.at(i);
+ auto child = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);
+ fillAccessibilityElement(child, childNode);
+ children[i] = {child, childNode};
+ }
+ if (!recursive)
+ return;
+ for (auto &child : children) {
+ fillChildrenAccessibilityElement(child.first, elementList, child.second, recursive);
+ }
+}
+
+static QWindow *windowForId(const char* instanceId)
+{
+ QWindow *window = nullptr;
+ if (instanceId == nullptr) {
+ window = QGuiApplication::focusWindow();
+ if (window == nullptr) {
+ window = QGuiApplication::topLevelWindows().empty() ? nullptr : QGuiApplication::topLevelWindows().last();
+ }
+ } else {
+ QOhPlatformWindow *pw = QOhWindowContext::get(instanceId);
+ if (pw != nullptr)
+ window = pw->window();
+ }
+ return window;
+}
+
+static void createOhNodeForWindow(QWindow *w, ArkUI_AccessibilityElementInfoList *elementList)
+{
+ auto node = QOhAccessibleNode::node(w);
+ if (node == nullptr) {
+ node = QtOh::runOnQtMainThreadWithResult([w]{
+ QOhAccessibleNode *node = new QOhAccessibleNode(w);
+ return node;
+ });
+ }
+
+ if (node != nullptr) {
+ auto element = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);
+ fillAccessibilityElement(element, node);
+ fillChildrenAccessibilityElement(element, elementList, node, true);
+ }
+}
+
+
+int32_t findAccessibilityNodeInfosById(const char* instanceId, int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ // Ignoring all requests while starting up / shutting down
+ if (QCoreApplication::startingUp() || QCoreApplication::closingDown())
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+
+ auto accessibilityProvider = QGuiApplicationPrivate::platformIntegration()->accessibility();
+ if (accessibilityProvider == nullptr)
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+ accessibilityProvider->setActive(true);
+
+ LOGI("QOhAccessibilityProvider::findAccessibilityNodeInfosById start, instanceId: %{public}s elementId: %{public}ld, requestId: %{public}d, mode: %{public}d",
+ instanceId, elementId, requestId, static_cast<int32_t>(mode));
+
+ if (elementList == nullptr) {
+ LOGI("QOhAccessibilityProvider::findAccessibilityNodeInfosById elementList is null");
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+ }
+
+ if (elementId == -1 && mode == ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_RECURSIVE_CHILDREN) {
+ if (instanceId == nullptr)
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+ QWindow *window = windowForId(instanceId);
+ if (window == nullptr || window->accessibleRoot() == nullptr)
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+ createOhNodeForWindow(window, elementList);
+ } else {
+ // Screen Reader will query Elements with mode==ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CURRENT
+ if (mode == ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CURRENT) {
+ QOhAccessibleNode *node = QOhAccessibleNode::find(elementId);
+ if (node != nullptr) {
+ auto element = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);
+ fillAccessibilityElement(element, node);
+ fillChildrenAccessibilityElement(element, elementList, node, true);
+ }
+ }
+ }
+
+ LOGI("QOhAccessibilityProvider::findAccessibilityNodeInfosById end");
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t findAccessibilityNodeInfosByText(const char* instanceId, int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t findFocusedAccessibilityNode(const char* instanceId, int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+
+{
+ LOGI("QOhAccessibilityProvider::findFocusedAccessibilityNode elementId: %{public}ld, requestId: %{public}d, focusType: %{public}d",
+ elementId, requestId, static_cast<int32_t>(focusType));
+
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t findNextFocusAccessibilityNode(const char* instanceId, int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+{
+
+ LOGI("QOhAccessibilityProvider::findNextFocusAccessibilityNode elementId: %{public}ld, requestId: %{public}d, direction: %{public}d",
+ elementId, requestId, static_cast<int32_t>(direction));
+
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t executeAccessibilityAction(const char* instanceId, int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId)
+{
+
+ Q_UNUSED(instanceId)
+ // Screen Reader will trigger this callback.
+ // At least the following actions must be processed
+ // After the processing, the response event needs to be sent to the system.
+ LOGI("QOhAccessibilityProvider::executeAccessibilityAction elementId: %{public}ld, action: %{public}d", elementId, action);
+ auto node = QOhAccessibleNode::find(elementId);
+ if (node == nullptr)
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+
+ QAccessibleInterface *interface = node->face();
+ QAccessibleActionInterface *actionInterface = interface->actionInterface();
+ QOhAccessibilityProvider *p = QOhAccessibilityProvider::provider(interface);
+ if (!actionInterface || !p)
+ return OH_NATIVEXCOMPONENT_RESULT_FAILED;
+
+ switch (action) {
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK:
+ QtOh::runOnQtMainThread([interface, actionInterface]{
+ auto actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
+ if (actionNames.isEmpty())
+ return;
+ QAccessibleBridgeUtils::performEffectiveAction(interface, actionNames.first());
+ });
+ p->sendAccessibilityEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_CLICKED);
+ break;
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS:
+ QtOh::runOnQtMainThread([interface]{
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::setFocusAction());
+ });
+ p->sendAccessibilityEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUSED);
+ break;
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD:
+ QtOh::runOnQtMainThread([interface]{
+ auto actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
+ if (actionNames.isEmpty())
+ return;
+ if (actionNames.contains(QAccessibleActionInterface::scrollDownAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::scrollDownAction());
+ if (actionNames.contains(QAccessibleActionInterface::scrollRightAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::scrollRightAction());
+ if (actionNames.contains(QAccessibleActionInterface::increaseAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::increaseAction());
+ });
+ p->sendAccessibilityEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED);
+ break;
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD:
+ QtOh::runOnQtMainThread([interface]{
+ auto actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
+ if (actionNames.isEmpty())
+ return;
+ if (actionNames.contains(QAccessibleActionInterface::scrollUpAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::scrollUpAction());
+ if (actionNames.contains(QAccessibleActionInterface::scrollLeftAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::scrollLeftAction());
+ if (actionNames.contains(QAccessibleActionInterface::decreaseAction()))
+ QAccessibleBridgeUtils::performEffectiveAction(interface, QAccessibleActionInterface::decreaseAction());
+ });
+ p->sendAccessibilityEvent(node, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED);
+ break;
+ default:
+ break;
+ }
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t clearFocusedFocusAccessibilityNode(const char* instanceId)
+{
+ LOGI("QOhAccessibilityProvider::clearFocusedFocusAccessibilityNode");
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+
+int32_t getAccessibilityNodeCursorPosition(const char* instanceId, int64_t elementId, int32_t requestId, int32_t *index)
+{
+ LOGI("QOhAccessibilityProvider::getAccessibilityNodeCursorPosition");
+ return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
+}
+}
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
new file mode 100644
@@ -0,0 +1,41 @@
+#ifndef QOHACCESSIBILITY_H
+#define QOHACCESSIBILITY_H
+
+#include <QtGui/qtguiglobal.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+#if QT_CONFIG(accessibility)
+
+QT_BEGIN_NAMESPACE
+class QOhAccessibleNode;
+
+namespace QtOhAccessibility {
+
+void fillAccessibilityElement(ArkUI_AccessibilityElementInfo *element, QOhAccessibleNode *node);
+
+void fillChildrenAccessibilityElement(ArkUI_AccessibilityElementInfo *parentNode, ArkUI_AccessibilityElementInfoList *elementList, QOhAccessibleNode *node, bool recursive = false);
+
+int32_t findAccessibilityNodeInfosById(const char* instanceId, int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+int32_t findAccessibilityNodeInfosByText(const char* instanceId, int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+int32_t findFocusedAccessibilityNode(const char* instanceId, int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+int32_t findNextFocusAccessibilityNode(const char* instanceId, int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+int32_t executeAccessibilityAction(const char* instanceId, int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId);
+int32_t clearFocusedFocusAccessibilityNode(const char* instanceId);
+int32_t getAccessibilityNodeCursorPosition(const char* instanceId, int64_t elementId, int32_t requestId, int32_t *index);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
+
+#endif // QOHACCESSIBILITY_H
new file mode 100644
@@ -0,0 +1,348 @@
+#include "qohaccessibilityprovider.h"
+#include "qohaccessiblenode.h"
+#include "qohaccessibility.h"
+#include "qohobjectholder.h"
+#include "qohwindowcontext.h"
+
+#include <QtGui/qwindow.h>
+#include <QtGui/qguiapplication.h>
+#include <QtCore/qopenharmonydefines.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <QTimer>
+#include <private/qaccessiblecache_p.h>
+#if QT_CONFIG(accessibility)
+
+#include <QtGui/qaccessible.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhAccessibilityProvider::QOhAccessibilityProvider(OH_NativeXComponent *xcompoent)
+{
+ ArkUI_AccessibilityProvider *provider = nullptr;
+ int32_t ret = OH_NativeXComponent_GetNativeAccessibilityProvider(xcompoent, &provider);
+ if (provider == nullptr) {
+ LOGW("get accessibility provider is null");
+ return;
+ }
+
+ m_accessibilityProviderCallbacks.findAccessibilityNodeInfosById = findAccessibilityNodeInfosById;
+ m_accessibilityProviderCallbacks.findAccessibilityNodeInfosByText = findAccessibilityNodeInfosByText;
+ m_accessibilityProviderCallbacks.findFocusedAccessibilityNode = findFocusedAccessibilityNode;
+ m_accessibilityProviderCallbacks.findNextFocusAccessibilityNode = findNextFocusAccessibilityNode;
+ m_accessibilityProviderCallbacks.executeAccessibilityAction = executeAccessibilityAction;
+ m_accessibilityProviderCallbacks.clearFocusedFocusAccessibilityNode = clearFocusedFocusAccessibilityNode;
+ m_accessibilityProviderCallbacks.getAccessibilityNodeCursorPosition = getAccessibilityNodeCursorPosition;
+#if OHOS_SDK_VERSION > 14
+ char id[OH_XCOMPONENT_ID_LEN_MAX + 1] = { };
+ uint64_t id_length = OH_XCOMPONENT_ID_LEN_MAX + 1;
+ ret = OH_NativeXComponent_GetXComponentId(xcompoent, id, &id_length);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGW("QOhAccessibilityProvider::QOhAccessibilityProvider get xcompoent id failed");
+ return;
+ }
+ ret = OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance(id, provider, &m_accessibilityProviderCallbacks);
+#else
+ ret = OH_ArkUI_AccessibilityProviderRegisterCallback(provider, &m_accessibilityProviderCallbacks);
+#endif
+ if (ret != 0) {
+ LOGW("OH_ArkUI_AccessibilityProviderRegisterCallback failed");
+ return;
+ }
+ m_provider = provider;
+}
+
+QOhAccessibilityProvider::~QOhAccessibilityProvider()
+{
+
+}
+
+void QOhAccessibilityProvider::registerForWindow(QWindow *window)
+{
+ if (window == nullptr || m_providers.contains(window))
+ return;
+ QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
+ if (!window->handle())
+ return;
+ OH_NativeXComponent *xcomponent = reinterpret_cast<OH_NativeXComponent *>(platform->nativeResourceForWindow("nativeXComponent", window));
+ if (xcomponent == nullptr)
+ return;
+ QOhAccessibilityProvider *provider = new QOhAccessibilityProvider(xcomponent);
+ m_providers.insert(window, provider);
+ provider->connect(window, &QWindow::destroyed, provider, [window]{
+ if (m_providers.contains(window)) {
+ delete m_providers.take(window);
+ }
+ });
+}
+
+void QOhAccessibilityProvider::playSound(const QString &sound)
+{
+
+}
+
+QOhAccessibilityProvider *QOhAccessibilityProvider::provider(QAccessibleEvent *event)
+{
+ QAccessibleInterface *iface = event->accessibleInterface();
+ if (!iface || !iface->isValid())
+ return nullptr;
+
+ return provider(iface);
+}
+
+QOhAccessibilityProvider *QOhAccessibilityProvider::provider(QAccessibleInterface *iface)
+{
+ QWindow *window = QOhWindowContext::windowForInterface(iface);
+
+ if (!m_providers.contains(window))
+ registerForWindow(window);
+
+ return m_providers.value(window);
+}
+
+void QOhAccessibilityProvider::processAccessibilityEvent(QAccessibleEvent *event, ArkUI_AccessibilityEventType eventType)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ p->sendAccessibilityEvent(iface, eventType);
+}
+
+void QOhAccessibilityProvider::notifyFocusChange(QAccessibleEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ QAccessible::State s = iface->state();
+ p->sendAccessibilityEvent(iface, s.focused == 1 ? ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUSED :
+ ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUS_CLEARED);
+}
+
+void QOhAccessibilityProvider::notifyStateChange(QAccessibleStateChangeEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ }
+
+void QOhAccessibilityProvider::notifyValueChange(QAccessibleValueChangeEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ p->sendAccessibilityEvent(iface, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_TEXT_UPDATE);
+}
+
+void QOhAccessibilityProvider::notifyNameChange(QAccessibleEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ p->sendAccessibilityEvent(iface, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_TEXT_UPDATE);
+}
+
+void QOhAccessibilityProvider::notifySelectionChange(QAccessibleEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ p->sendAccessibilityEvent(iface, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SELECTED);
+}
+
+void QOhAccessibilityProvider::notifyTextChange(QAccessibleEvent *event)
+{
+ QOhAccessibilityProvider *p = provider(event);
+ if (p == nullptr)
+ return;
+ QAccessibleInterface *iface = event->accessibleInterface();
+ p->sendAccessibilityEvent(iface, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_TEXT_UPDATE);
+}
+
+void QOhAccessibilityProvider::deleteTimer()
+{
+ if (m_pageChangedTimer != nullptr) {
+ if (m_pageChangedTimer->isActive())
+ m_pageChangedTimer->stop();
+ delete m_pageChangedTimer;
+ }
+}
+
+void QOhAccessibilityProvider::notifyPageChange(QAccessibleEvent *event)
+{
+// Qt上层会发送多个hide/show事件下来
+ m_pedingInterfaces << QAccessible::uniqueId(event->accessibleInterface());
+ if (m_pageChangedTimer == nullptr) {
+ m_pageChangedTimer = new QTimer;
+ qRemovePostRoutine(deleteTimer);
+ m_pageChangedTimer->setSingleShot(true);
+ QObject::connect(m_pageChangedTimer, &QTimer::timeout, []{
+ if (m_pedingInterfaces.isEmpty())
+ return;
+ QList<QOhAccessibilityProvider *> pr;
+ for (int i = 0; i < m_pedingInterfaces.count(); ++i) {
+ auto id = m_pedingInterfaces.at(i);
+ auto iface = QAccessibleCache::instance()->interfaceForId(id);
+ if (iface == nullptr)
+ continue;
+ auto p = provider(iface);
+ if (p != nullptr && !pr.contains(p)) {
+ pr << p;
+ p->sendAccessibilityEvent(iface, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE);
+ }
+ }
+ m_pedingInterfaces.clear();
+ });
+ }
+ if (m_pageChangedTimer->isActive())
+ return;
+ m_pageChangedTimer->start(5000);
+}
+
+void QOhAccessibilityProvider::sendAccessibilityEvent(QOhAccessibleNode *node, ArkUI_AccessibilityEventType eventType)
+{
+ if (m_provider == nullptr) {
+ qWarning() << "sendAccessibilityEvent: ohos provider is nullptr";
+ return;
+ }
+ QOhObjectHolder<ArkUI_AccessibilityEventInfo> eventInfo(OH_ArkUI_CreateAccessibilityEventInfo, OH_ArkUI_DestoryAccessibilityEventInfo);
+ auto nativeEventInfo = eventInfo.object();
+ if (nativeEventInfo == nullptr) {
+ qWarning() << "sendAccessibilityEvent: Unable to create accessibility eventInfo";
+ return;
+ }
+ QOhObjectHolder<ArkUI_AccessibilityElementInfo> elementInfo(OH_ArkUI_CreateAccessibilityElementInfo, OH_ArkUI_DestoryAccessibilityElementInfo);
+ auto nativeElementInfo = elementInfo.object();
+ if (nativeElementInfo == nullptr) {
+ qWarning() << "sendAccessibilityEvent: Unable to create accessibility elementInfo";
+ return;
+ }
+ QtOhAccessibility::fillAccessibilityElement(nativeElementInfo, node);
+ auto callback = [](int32_t code){
+ qDebug() << "send accessibility result: " << code;
+ };
+ OH_ArkUI_AccessibilityEventSetEventType(nativeEventInfo, eventType);
+ OH_ArkUI_AccessibilityEventSetElementInfo(nativeEventInfo, nativeElementInfo);
+ OH_ArkUI_SendAccessibilityAsyncEvent(m_provider, nativeEventInfo, callback);
+}
+
+void QOhAccessibilityProvider::sendAccessibilityEvent(QAccessibleInterface *iface, ArkUI_AccessibilityEventType eventType)
+{
+ if (!iface || !iface->isValid())
+ return;
+ auto node = QOhAccessibleNode::find(iface);
+ if (node == nullptr)
+ return;
+ sendAccessibilityEvent(node, eventType);
+}
+
+#if OHOS_SDK_VERSION > 14
+int32_t QOhAccessibilityProvider::findAccessibilityNodeInfosById(const char* instanceId, int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ return QtOhAccessibility::findAccessibilityNodeInfosById(instanceId, elementId, mode,requestId, elementList);
+}
+
+int32_t QOhAccessibilityProvider::findAccessibilityNodeInfosByText(const char* instanceId, int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ return QtOhAccessibility::findAccessibilityNodeInfosByText(instanceId, elementId, text, requestId, elementList);
+}
+
+int32_t QOhAccessibilityProvider::findFocusedAccessibilityNode(const char* instanceId, int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+
+{
+ return QtOhAccessibility::findFocusedAccessibilityNode(instanceId, elementId, focusType, requestId, elementInfo);
+}
+
+int32_t QOhAccessibilityProvider::findNextFocusAccessibilityNode(const char* instanceId, int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+{
+
+ return QtOhAccessibility::findNextFocusAccessibilityNode(instanceId, elementId, direction, requestId, elementInfo);
+}
+
+int32_t QOhAccessibilityProvider::executeAccessibilityAction(const char* instanceId, int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId)
+{
+
+ return QtOhAccessibility::executeAccessibilityAction(instanceId, elementId, action,
+ actionArguments,
+ requestId);
+}
+
+int32_t QOhAccessibilityProvider::clearFocusedFocusAccessibilityNode(const char* instanceId)
+{
+ return QtOhAccessibility::clearFocusedFocusAccessibilityNode(instanceId);
+}
+
+int32_t QOhAccessibilityProvider::getAccessibilityNodeCursorPosition(const char* instanceId, int64_t elementId, int32_t requestId, int32_t *index)
+{
+ return QtOhAccessibility::getAccessibilityNodeCursorPosition(instanceId, elementId, requestId, index);
+}
+#else
+int32_t QOhAccessibilityProvider::findAccessibilityNodeInfosById(int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ return QtOhAccessibility::findAccessibilityNodeInfosById(nullptr, elementId, mode,requestId, elementList);
+}
+
+int32_t QOhAccessibilityProvider::findAccessibilityNodeInfosByText(int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList)
+{
+ return QtOhAccessibility::findAccessibilityNodeInfosByText(nullptr, elementId, text, requestId, elementList);
+}
+
+int32_t QOhAccessibilityProvider::findFocusedAccessibilityNode(int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+
+{
+ return QtOhAccessibility::findFocusedAccessibilityNode(nullptr, elementId, focusType, requestId, elementInfo);
+}
+
+int32_t QOhAccessibilityProvider::findNextFocusAccessibilityNode(int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo)
+{
+
+ return QtOhAccessibility::findNextFocusAccessibilityNode(nullptr, elementId,
+ direction,
+ requestId,
+ elementInfo);
+}
+
+int32_t QOhAccessibilityProvider::executeAccessibilityAction(int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId)
+{
+
+ return QtOhAccessibility::executeAccessibilityAction(nullptr, elementId, action,
+ actionArguments,
+ requestId);
+}
+
+int32_t QOhAccessibilityProvider::clearFocusedFocusAccessibilityNode()
+{
+ return QtOhAccessibility::clearFocusedFocusAccessibilityNode(nullptr);
+}
+
+int32_t QOhAccessibilityProvider::getAccessibilityNodeCursorPosition(int64_t elementId, int32_t requestId, int32_t *index)
+{
+ return QtOhAccessibility::getAccessibilityNodeCursorPosition(nullptr, elementId, requestId, index);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
new file mode 100644
@@ -0,0 +1,94 @@
+#ifndef QOHACCESSIBILITYPROVIDER_H
+#define QOHACCESSIBILITYPROVIDER_H
+
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+#include <QtGui/qaccessible.h>
+#include <QtCore/qhash.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAccessibleInterface;
+class QOhAccessibleNode;
+class QOhAccessibilityProvider : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QOhAccessibilityProvider(OH_NativeXComponent *xcomponent);
+ virtual ~QOhAccessibilityProvider();
+
+ static void registerForWindow(QWindow *window);
+
+ static void playSound(const QString &sound);
+ static void processAccessibilityEvent(QAccessibleEvent *event, ArkUI_AccessibilityEventType eventType);
+ static void notifyFocusChange(QAccessibleEvent *event);
+ static void notifyStateChange(QAccessibleStateChangeEvent *event);
+ static void notifyValueChange(QAccessibleValueChangeEvent *event);
+ static void notifyNameChange(QAccessibleEvent *event);
+ static void notifySelectionChange(QAccessibleEvent *event);
+ static void notifyTextChange(QAccessibleEvent *event);
+ static void notifyPageChange(QAccessibleEvent *event);
+
+ virtual void sendAccessibilityEvent(QAccessibleInterface *iface, ArkUI_AccessibilityEventType eventType);
+ virtual void sendAccessibilityEvent(QOhAccessibleNode *node, ArkUI_AccessibilityEventType eventType);
+
+ static QOhAccessibilityProvider *provider(QAccessibleEvent *event);
+ static QOhAccessibilityProvider *provider(QAccessibleInterface *iface);
+private:
+#if OHOS_SDK_VERSION > 14
+ static int32_t findAccessibilityNodeInfosById(const char* instanceId, int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+ static int32_t findAccessibilityNodeInfosByText(const char* instanceId, int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+ static int32_t findFocusedAccessibilityNode(const char* instanceId, int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+ static int32_t findNextFocusAccessibilityNode(const char* instanceId, int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+ static int32_t executeAccessibilityAction(const char* instanceId, int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId);
+ static int32_t clearFocusedFocusAccessibilityNode(const char* instanceId);
+ static int32_t getAccessibilityNodeCursorPosition(const char* instanceId, int64_t elementId, int32_t requestId, int32_t *index);
+#else
+ static int32_t findAccessibilityNodeInfosById(int64_t elementId, ArkUI_AccessibilitySearchMode mode,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+ static int32_t findAccessibilityNodeInfosByText(int64_t elementId, const char *text, int32_t requestId,
+ ArkUI_AccessibilityElementInfoList *elementList);
+ static int32_t findFocusedAccessibilityNode(int64_t elementId, ArkUI_AccessibilityFocusType focusType,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+ static int32_t findNextFocusAccessibilityNode(int64_t elementId,
+ ArkUI_AccessibilityFocusMoveDirection direction,
+ int32_t requestId,
+ ArkUI_AccessibilityElementInfo *elementInfo);
+ static int32_t executeAccessibilityAction(int64_t elementId, ArkUI_Accessibility_ActionType action,
+ ArkUI_AccessibilityActionArguments *actionArguments,
+ int32_t requestId);
+ static int32_t clearFocusedFocusAccessibilityNode();
+ static int32_t getAccessibilityNodeCursorPosition(int64_t elementId, int32_t requestId, int32_t *index);
+#endif
+ static void createOhNodeForWindow(QWindow *w, ArkUI_AccessibilityElementInfoList *elementList);
+private:
+ ArkUI_AccessibilityProvider *m_provider = nullptr;
+#if OHOS_SDK_VERSION > 14
+ ArkUI_AccessibilityProviderCallbacksWithInstance m_accessibilityProviderCallbacks;
+#else
+ ArkUI_AccessibilityProviderCallbacks m_accessibilityProviderCallbacks;
+#endif
+ static inline QHash<QWindow *, QOhAccessibilityProvider *> m_providers = {};
+ static void deleteTimer();
+ static inline QTimer *m_pageChangedTimer = nullptr;
+ static inline QList<QAccessible::Id> m_pedingInterfaces = {};
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
+
+#endif // QOHACCESSIBILITYPROVIDER_H
new file mode 100644
@@ -0,0 +1,427 @@
+#include "qohwindowcontext.h"
+#include "qohaccessiblenode.h"
+
+#include <limits>
+#include <qopenharmony.h>
+#include <private/qaccessiblecache_p.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtCore/qmetaobject.h>
+#include <QWindow>
+#include <private/qaccessiblebridgeutils_p.h> /* Qt6 only modify */
+
+QT_BEGIN_NAMESPACE
+
+static ArkUI_AccessibleAction qtAction2OhAction(const QString &name)
+{
+ ArkUI_AccessibleAction action;
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_INVALID;
+ action.description = nullptr;
+ if (name == QAccessibleActionInterface::pressAction()
+ || name == QAccessibleActionInterface::showMenuAction()
+ || name == QAccessibleActionInterface::toggleAction()) {
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK;
+ } else if (name == QAccessibleActionInterface::setFocusAction()) {
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS;
+ } else if (name == QAccessibleActionInterface::scrollUpAction()
+ || name == QAccessibleActionInterface::scrollLeftAction()
+ || name == QAccessibleActionInterface::decreaseAction()) {
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD;
+ } else if (name == QAccessibleActionInterface::scrollDownAction()
+ || name == QAccessibleActionInterface::increaseAction()
+ || name == QAccessibleActionInterface::scrollRightAction()) {
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD;
+ } else if (name == QAccessibleActionInterface::nextPageAction()) {
+
+ } else if (name == QAccessibleActionInterface::previousPageAction()) {
+
+ }
+ return action;
+}
+
+QOhAccessibleNode::QOhAccessibleNode(QWindow *window)
+ : m_parent(nullptr)
+ , m_window(window)
+{
+ QAccessibleInterface *accessible = window->accessibleRoot();
+ if (accessible != nullptr) {
+ m_id = QAccessible::uniqueId(accessible);
+ createNode(accessible);
+ } else {
+ qWarning() << "the accessible is nullptr" << window;
+ }
+ AllNodes.insert(m_id, this);
+ RootNodes.insert(window, this);
+ if (m_parent != nullptr)
+ m_parent->m_children << this;
+
+ QObject::connect(window, &QWindow::destroyed, window, [id = m_id]() {
+ removeNode(id);
+ });
+}
+
+QOhAccessibleNode::QOhAccessibleNode(QWindow *window, QAccessibleInterface *accessible, QOhAccessibleNode *parent)
+ : m_parent(parent)
+ , m_window(window)
+{
+ if (accessible == nullptr)
+ return;
+ m_id = QAccessible::uniqueId(accessible);
+ AllNodes.insert(m_id, this);
+ createNode(accessible);
+ if (m_parent != nullptr)
+ m_parent->m_children << this;
+}
+
+QOhAccessibleNode::~QOhAccessibleNode()
+{
+ qDeleteAll(m_children);
+ m_children.clear();
+ AllNodes.remove(m_id);
+ if (m_parent == nullptr)
+ RootNodes.remove(m_window);
+ else {
+ m_parent->m_children.removeOne(this);
+ }
+ clearDatas();
+ m_window = nullptr;
+}
+
+QOhAccessibleNode *QOhAccessibleNode::node(QWindow *w)
+{
+ return RootNodes.value(w);
+}
+
+void QOhAccessibleNode::removeNode(QAccessible::Id id)
+{
+ if (AllNodes.contains(id))
+ delete AllNodes.take(id);
+}
+
+int64_t QOhAccessibleNode::id() const
+{
+ constexpr auto minAllowedNodeId = std::numeric_limits<std::uint32_t>::max() - std::numeric_limits<std::int32_t>::max();
+
+ if (m_id < minAllowedNodeId) {
+ // error
+ return 0;
+ }
+ return m_id - minAllowedNodeId;
+}
+
+QString QOhAccessibleNode::type() const
+{
+ return m_type;
+}
+
+void QOhAccessibleNode::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString QOhAccessibleNode::name() const
+{
+ return m_name;
+}
+
+void QOhAccessibleNode::setContents(const QString &contents)
+{
+ m_contents = contents;
+}
+
+QString QOhAccessibleNode::contents() const
+{
+ return m_contents;
+}
+
+void QOhAccessibleNode::setHint(const QString &hint)
+{
+ m_hint = hint;
+}
+
+QString QOhAccessibleNode::hint() const
+{
+ return m_hint;
+}
+
+void QOhAccessibleNode::setState(QAccessible::State state)
+{
+ m_state = state;
+}
+
+QAccessible::State QOhAccessibleNode::state() const
+{
+ return m_state;
+}
+
+void QOhAccessibleNode::setGeometry(const QRect &r)
+{
+ m_geometry = { .leftTopX = r.left(),
+ .leftTopY = r.top(),
+ .rightBottomX = r.right(),
+ .rightBottomY = r.bottom() };
+}
+
+ArkUI_AccessibleRect QOhAccessibleNode::geometry() const
+{
+ return m_geometry;
+}
+
+void QOhAccessibleNode::setParent(QOhAccessibleNode *parent)
+{
+ if (parent == nullptr || m_parent == parent)
+ return;
+ if (m_parent != nullptr)
+ m_parent->m_children.removeOne(this);
+ m_parent = parent;
+ m_parent->m_children << this;
+}
+
+QOhAccessibleNode *QOhAccessibleNode::parent() const
+{
+ return m_parent;
+}
+
+const QList<QOhAccessibleNode *> &QOhAccessibleNode::children() const
+{
+ return m_children;
+}
+
+QOhAccessibleNode *QOhAccessibleNode::find(QAccessibleInterface *accessible)
+{
+ if (accessible == nullptr)
+ return nullptr;
+ auto id = QAccessible::uniqueId(accessible);
+ auto iter = AllNodes.find(id);
+ return iter != AllNodes.end() ? iter.value() : nullptr;
+}
+
+QOhAccessibleNode *QOhAccessibleNode::find(int64_t id)
+{
+ auto nodes = AllNodes.values();
+ for (int i = 0; i < nodes.count(); ++i) {
+ if (nodes.at(i)->id() == id)
+ return nodes.at(i);
+ }
+ return nullptr;
+}
+
+QAccessibleInterface *QOhAccessibleNode::face() const
+{
+ return QAccessibleCache::instance()->interfaceForId(m_id);
+}
+
+QString QOhAccessibleNode::accelerator() const
+{
+ return m_accelerator;
+}
+
+std::vector<ArkUI_AccessibleAction> QOhAccessibleNode::actions() const
+{
+ return m_actions;
+}
+
+std::optional<ArkUI_AccessibleRangeInfo> QOhAccessibleNode::valueInfo() const
+{
+ return m_valueInfo;
+}
+
+std::optional<ArkUI_AccessibleGridInfo> QOhAccessibleNode::gridInfo() const
+{
+ return m_gridInfo;
+}
+
+std::vector<ArkUI_AccessibleGridItemInfo> QOhAccessibleNode::gridItemInfo() const
+{
+ return m_gridItemInfo;
+}
+
+QColor QOhAccessibleNode::backgroundColor() const
+{
+ return m_backgroundColor;
+}
+
+std::vector<std::pair<int32_t, int32_t> > QOhAccessibleNode::selections() const
+{
+ return m_selections;
+}
+
+bool QOhAccessibleNode::isVisible() const
+{
+ if (state().invisible == 1) {
+ return false;
+ }
+ return m_geometry.rightBottomX > m_geometry.leftTopX
+ && m_geometry.rightBottomY > m_geometry.leftTopY;
+}
+
+QRect QOhAccessibleNode::getRelativeGeometry(QAccessibleInterface *accessible, QWindow *window)
+{
+ if (window == nullptr) {
+ window = QOhWindowContext::windowForInterface(accessible);
+ }
+ QRect r = accessible->rect();
+ if (window == nullptr) {
+ return r;
+ }
+ window = QOhWindowContext::topLevelOf(window, QWindow::IncludeTransients);
+ // 15的坐标是相对于窗口的坐标,之前的版本是屏幕坐标
+ if (QtOh::apiVersion() > 14) {
+ QPoint pt = window->mapFromGlobal(r.topLeft());
+ r.moveTopLeft(pt);
+ } else {
+ int titleHeight = window->frameMargins().top();
+ r.translate(0, -titleHeight);
+ }
+ if (QHighDpiScaling::isActive())
+ r = QHighDpi::toNativePixels(r, window);
+ return r;
+}
+
+void QOhAccessibleNode::update()
+{
+ auto accessible = QAccessibleCache::instance()->interfaceForId(m_id);
+ if (!accessible || !m_window)
+ return;
+
+ m_name = accessible->text(QAccessible::Name);
+ m_contents = accessible->text(QAccessible::Value);
+ m_hint = accessible->text(QAccessible::Help);
+ m_description = accessible->text(QAccessible::Description);
+ m_accelerator = accessible->text(QAccessible::Accelerator);
+ m_backgroundColor = accessible->backgroundColor();
+ m_state = accessible->state();
+ setGeometry(getRelativeGeometry(accessible, m_window));
+
+ clearDatas();
+ QStringList actions = QAccessibleBridgeUtils::effectiveActionNames(accessible);
+ QAccessibleActionInterface *ac = accessible->actionInterface();
+ for (int i = 0; i < actions.count(); ++i) {
+ auto ohac = qtAction2OhAction(actions.at(i));
+ if (ohac.actionType != ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_INVALID) {
+ QString actionDesc = ac ? ac->localizedActionDescription(actions.at(i)) : QString();
+ switch (ohac.actionType) {
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK:
+ if (!clickable) {
+ clickable = true;
+ ohac.description = strdup(actionDesc.toUtf8().constData());
+ m_actions.push_back(ohac);
+ }
+ break;
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS:
+ if (!focusable) {
+ focusable = true;
+ ohac.description = strdup(actionDesc.toUtf8().constData());
+ m_actions.push_back(ohac);
+ }
+ break;
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_FORWARD:
+ [[fallthrough]];
+ case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_SCROLL_BACKWARD:
+ scrollable = true;
+ ohac.description = strdup(actionDesc.toUtf8().constData());
+ m_actions.push_back(ohac);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (!focusable && (clickable || scrollable)) {
+ focusable = true;
+ ArkUI_AccessibleAction action;
+ action.actionType = ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS;
+ // 使用nullptr会引起OHOS::Ace::Framework::TransformAccessbilityElementInfo崩溃
+ // action.description = nullptr;
+ action.description = strdup("");
+ m_actions.push_back(action);
+ }
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (valueInterface != nullptr) {
+ m_valueInfo = { .min = valueInterface->minimumValue().toDouble(),
+ .max = valueInterface->maximumValue().toDouble(),
+ .current = valueInterface->currentValue().toDouble()};
+ }
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (tableInterface != nullptr) {
+ m_gridInfo = { .rowCount = tableInterface->rowCount(),
+ .columnCount = tableInterface->columnCount(),
+ .selectionMode = 1 };
+ for (int i = 0; i < tableInterface->rowCount(); ++i) {
+ for (int j = 0; j < tableInterface->columnCount(); ++j) {
+ QAccessibleInterface *item = tableInterface->cellAt(i, j);
+ if (item == nullptr)
+ continue;
+ QAccessibleTableCellInterface *cellInterface = (QAccessibleTableCellInterface *)item->interface_cast(QAccessible::TableCellInterface);
+ ArkUI_AccessibleGridItemInfo info = {
+ .heading = false,
+ .selected = cellInterface->isSelected(),
+ .columnIndex = j,
+ .rowIndex = i,
+ .columnSpan = cellInterface->rowExtent(),
+ .rowSpan = cellInterface->columnExtent()
+ };
+ m_gridItemInfo.push_back(info);
+ }
+ }
+ }
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (textInterface != nullptr) {
+ int count = textInterface->selectionCount();
+ for (int i = 0; i < count; ++i) {
+ int start = 0;
+ int end = 0;
+ textInterface->selection(i, &start, &end);
+ m_selections.push_back(std::make_pair((int32_t)start, (int32_t)end));
+ }
+ }
+
+ for (int i = 0; i < accessible->childCount(); ++i) {
+ auto child = accessible->child(i);
+ if (auto childNode = find(child)) {
+ if (childNode->parent() != this) {
+ childNode->setParent(this);
+ }
+ continue;
+ }
+ new QOhAccessibleNode(m_window, child, this);
+ }
+}
+
+QString QOhAccessibleNode::description() const
+{
+ return m_description;
+}
+
+static const char *roleName(QAccessible::Role role) {
+ static const QMetaObject *metaObject = qt_getEnumMetaObject(role);
+ static const QMetaEnum me = metaObject->enumerator(metaObject->indexOfEnumerator(qt_getEnumName(role)));
+ return me.valueToKey(role);
+}
+
+void QOhAccessibleNode::createNode(QAccessibleInterface *accessible)
+{
+ if (QObject *obj = accessible->object()) {
+ const QMetaObject *mo = obj->metaObject();
+ m_type = mo->className();
+ }
+ if (m_type.isEmpty()) {
+ m_type = QString::fromLatin1(roleName(accessible->role()));
+ }
+ update();
+}
+
+void QOhAccessibleNode::clearDatas()
+{
+ for (size_t i = 0; i < m_actions.size(); ++i) {
+ if (m_actions[i].description != nullptr)
+ free((void *)m_actions[i].description);
+ }
+ m_gridItemInfo.clear();
+ m_actions.clear();
+ m_selections.clear();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,103 @@
+#ifndef QOHACCESSIBLENODE_H
+#define QOHACCESSIBLENODE_H
+#include <QList>
+#include <QString>
+#include <QScopedPointer>
+#include <QAccessibleInterface>
+#include <arkui/native_interface_accessibility.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhAccessibleNode
+{
+public:
+ explicit QOhAccessibleNode(QWindow *window);
+ QOhAccessibleNode(QWindow *window, QAccessibleInterface *accessible, QOhAccessibleNode *parent);
+ virtual ~QOhAccessibleNode();
+
+ static QOhAccessibleNode *node(QWindow *w);
+ static void removeNode(QAccessible::Id);
+ int64_t id() const;
+
+ QString type() const;
+
+ void setName(const QString &name);
+ QString name() const;
+
+ void setContents(const QString &contents);
+ QString contents() const;
+
+ void setHint(const QString &hint);
+ QString hint() const;
+
+ void setState(QAccessible::State state);
+ QAccessible::State state() const;
+
+ void setGeometry(const QRect &geometry);
+ ArkUI_AccessibleRect geometry() const;
+
+ void setParent(QOhAccessibleNode *parent);
+ QOhAccessibleNode *parent() const;
+
+ const QList<QOhAccessibleNode *> &children() const;
+
+ static QOhAccessibleNode *find(int64_t id);
+ static QOhAccessibleNode *find(QAccessibleInterface *accessible);
+
+ QAccessibleInterface *face() const;
+
+ QString description() const;
+
+ QString accelerator() const;
+
+ std::vector<ArkUI_AccessibleAction> actions() const;
+
+ std::optional<ArkUI_AccessibleRangeInfo> valueInfo() const;
+
+ std::optional<ArkUI_AccessibleGridInfo> gridInfo() const;
+
+ std::vector<ArkUI_AccessibleGridItemInfo> gridItemInfo() const;
+
+ QColor backgroundColor() const;
+
+ std::vector<std::pair<int32_t, int32_t>> selections() const;
+
+ bool isVisible() const;
+
+ static QRect getRelativeGeometry(QAccessibleInterface *accessible, QWindow *window);
+
+ void update();
+
+ bool clickable = false;
+ bool focusable = false;
+ bool scrollable = false;
+private:
+ void createNode(QAccessibleInterface *accessible);
+ void clearDatas();
+private:
+ QOhAccessibleNode *m_parent;
+ QWindow *m_window;
+ QAccessible::Id m_id;
+ QString m_type;
+ QString m_name;
+ QString m_contents;
+ QString m_description;
+ QString m_accelerator;
+ QString m_hint;
+ QAccessible::State m_state;
+ QColor m_backgroundColor;
+ ArkUI_AccessibleRect m_geometry;
+ std::vector<ArkUI_AccessibleAction> m_actions;
+ std::optional<ArkUI_AccessibleRangeInfo> m_valueInfo;
+ std::optional<ArkUI_AccessibleGridInfo> m_gridInfo;
+ std::vector<ArkUI_AccessibleGridItemInfo> m_gridItemInfo;
+ std::vector<std::pair<int32_t, int32_t>> m_selections;
+ QList<QOhAccessibleNode *> m_children;
+
+ static inline QHash<QAccessible::Id, QOhAccessibleNode *> AllNodes = {};
+ static inline QHash<QWindow*, QOhAccessibleNode *> RootNodes = {};
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHACCESSIBLENODE_H
new file mode 100644
@@ -0,0 +1,218 @@
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+
+#include "qohplatformaccessibility.h"
+#include "qohaccessibilityprovider.h"
+#include <QtGui/qaccessible.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <private/qaccessiblecache_p.h>
+#include <qpa/qplatformintegration.h>
+#include "qohauxiliary.h"
+#include "qohwindowcontext.h"
+#include "qohaccessiblenode.h"
+#include "qohplatformaccessibility.h"
+#include "qohaccessibilityprovider.h"
+QT_BEGIN_NAMESPACE
+
+namespace {
+void updateAccessibilityLater(QObject *qObject, QAccessible::Event accessibleEventType)
+{
+ QtOh::runOnQtMainThreadLater([qObject = QPointer<QObject>(qObject), accessibleEventType]() {
+ if (qObject.isNull()) {
+ return;
+ }
+ QAccessibleEvent accessibleEvent(qObject, accessibleEventType);
+ QAccessible::updateAccessibility(&accessibleEvent);
+ });
+}
+
+std::optional<ArkUI_AccessibilityEventType> tryMapQAccessibleEventToAccessibilityEvent(
+ QAccessibleEvent *event)
+{
+ auto *obj = event->object();
+ bool isTopWindow = obj && obj->isWindowType();
+ switch (event->type()) {
+ case QAccessible::ObjectCreated:
+ case QAccessible::ObjectDestroyed:
+ case QAccessible::ParentChanged:
+ case QAccessible::PageChanged:
+ case QAccessible::ObjectReorder:
+ case QAccessible::ObjectShow:
+ case QAccessible::ObjectHide:
+ case QAccessible::LocationChanged:
+ return {isTopWindow ? ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE : ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE};
+ case QAccessible::Focus:
+ {
+ QAccessibleInterface *iface = event->accessibleInterface();
+ QAccessible::State s = iface->state();
+ return {s.focused == 1 ? ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUSED : ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUS_CLEARED};
+ }
+ case QAccessible::ScrollingEnd:
+ return {ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SCROLLED};
+ case QAccessible::SelectionAdd:
+ return {ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_SELECTED};
+ case QAccessible::NameChanged:
+ case QAccessible::TextAttributeChanged:
+ case QAccessible::TextColumnChanged:
+ case QAccessible::TextInserted:
+ case QAccessible::TextRemoved:
+ case QAccessible::TextUpdated:
+ case QAccessible::TextSelectionChanged:
+ case QAccessible::TextCaretMoved:
+ return {ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_TEXT_UPDATE};
+
+ default:
+ break;
+ }
+
+ return {};
+}
+
+
+class AccessibilityEventFilter : public QObject
+{
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+};
+
+bool AccessibilityEventFilter::eventFilter(QObject *qObject, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::Create: {
+ updateAccessibilityLater(qObject, QAccessible::ObjectCreated);
+ break;
+ }
+ case QEvent::Resize:
+ updateAccessibilityLater(qObject, QAccessible::LocationChanged);
+ break;
+ case QEvent::Move: {
+ // 由于鸿蒙无障碍框架接收的是基于XC的坐标,因此窗体位置移动不需要做更新
+ if (qObject->isWindowType()) {
+ break;
+ }
+ updateAccessibilityLater(qObject, QAccessible::LocationChanged);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return QObject::eventFilter(qObject, event);
+}
+}
+
+QOhPlatformAccessibility::QOhPlatformAccessibility()
+{
+ QCoreApplication::instance()->installEventFilter(new AccessibilityEventFilter());
+}
+
+QOhPlatformAccessibility::~QOhPlatformAccessibility()
+{
+}
+
+static QAccessibleInterface *topLevelAccessibleInterface(QAccessibleInterface *accessible)
+{
+ auto *result = accessible;
+ while (result != nullptr && result->parent() != nullptr && result->parent()->role() != QAccessible::Application) {
+ result = result->parent();
+ }
+ return result;
+}
+// Handles accessibility update notifications.
+void QOhPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
+{
+ bool enable = QtOh::isEnvironmentVariableIsTrue("QT_ENABLE_ACCESSIBILITY", false);
+ if (!enable || !event)
+ return;
+
+ switch (event->type()) {
+ case QAccessible::PopupMenuStart:
+ QOhAccessibilityProvider::playSound("MenuStart");
+ break;
+ case QAccessible::MenuCommand:
+ QOhAccessibilityProvider::playSound("MenuCommand");
+ break;
+ case QAccessible::Alert:
+ QOhAccessibilityProvider::playSound("Alert");
+ break;
+ default:
+ break;
+ }
+
+ QAccessibleInterface *accessible = event->accessibleInterface();
+ if (accessible == nullptr) {
+ return;
+ }
+
+ if (!accessible->isValid()) {
+ if (event->type() == QAccessible::ObjectDestroyed) {
+ QOhAccessibleNode::removeNode(event->uniqueId());
+ }
+ return;
+ }
+
+ auto createNode = [](QAccessibleInterface *accessible) {
+ QOhAccessibleNode *node = nullptr;
+ if (QAccessibleCache::instance()->idForInterface(accessible) == 0)
+ return node;
+ auto parent = accessible->parent();
+ QAccessibleInterface *topLevel = topLevelAccessibleInterface(accessible);
+ QOhAccessibleNode *nodeParent = QOhAccessibleNode::find(topLevel);
+ QWindow *window = QOhWindowContext::windowForInterface(topLevel);
+ if (window != nullptr) {
+ window = QOhWindowContext::topLevelOf(window);
+ if (nodeParent == nullptr) {
+ nodeParent = new QOhAccessibleNode(window);
+ }
+ /* 创建顶层节点时,childCount可能不对, Qt bug?
+ * QGraphicsScene scene;
+ * QGraphicsView view(&scene);
+ * QPushButton *pushButton = new QPushButton("ttt", &view);
+ *
+ * QGraphicsView的child没有包含pushButton, 但pushButton的parent是QGraphicsView
+ * 因此需要在下面创建子节点
+ */
+ node = QOhAccessibleNode::find(accessible);
+ if (node == nullptr) {
+ node = new QOhAccessibleNode(window, accessible, nodeParent);
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "create a11y node error";
+ }
+ return node;
+ };
+
+ QOhAccessibleNode *node = QOhAccessibleNode::find(accessible);
+ if (node == nullptr) {
+ if (event->type() == QAccessible::ObjectCreated || event->type() == QAccessible::ObjectShow) {
+ createNode(accessible);
+ }
+ }
+
+ if (node == nullptr) {
+ qDebug() << Q_FUNC_INFO << "get a11y node failed";
+ // error
+ return;
+ }
+
+ switch (event->type()) {
+ case QAccessible::ParentChanged:
+ QOhAccessibleNode::removeNode(event->uniqueId());
+ createNode(accessible);
+ default:
+ break;
+ }
+ auto accessibilityEvent = tryMapQAccessibleEventToAccessibilityEvent(event);
+ if (accessibilityEvent.has_value()) {
+ if (*accessibilityEvent == ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_CONTENT_UPDATE) {
+ QOhAccessibilityProvider::notifyPageChange(event);
+ } else {
+ QOhAccessibilityProvider::processAccessibilityEvent(event, *accessibilityEvent);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
new file mode 100644
@@ -0,0 +1,23 @@
+#ifndef QOHPLATFORMACCESSIBILITY_H
+#define QOHPLATFORMACCESSIBILITY_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/QMultiHash>
+#if QT_CONFIG(accessibility)
+
+#include <qpa/qplatformaccessibility.h>
+
+QT_BEGIN_NAMESPACE
+class QOhPlatformAccessibility : public QPlatformAccessibility
+{
+public:
+ explicit QOhPlatformAccessibility();
+ virtual ~QOhPlatformAccessibility();
+ void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
+
+#endif // QOHPLATFORMACCESSIBILITY_H
new file mode 100644
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qohdragevent.h
+
+
+SOURCES += \
+ $$PWD/qohdragevent.cpp
new file mode 100644
@@ -0,0 +1,119 @@
+#include "qohdragevent.h"
+#include "qohkeys.h" /* Qt 6 add */
+#include "qohauxiliary.h"
+#include "qohobjectholder.h"
+#include <qopenharmonydefines.h>
+#include <database/udmf/udmf_meta.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhDragEvent::QOhDragEvent(ArkUI_DragEvent *event)
+ : m_dragEvent(event)
+{
+
+}
+
+
+
+Qt::DropActions QOhDragEvent::actions() const
+{
+ ArkUI_DropOperation operation;
+ if (ARKUI_ERROR_CODE_NO_ERROR != OH_ArkUI_DragEvent_GetDropOperation(m_dragEvent, &operation))
+ return Qt::IgnoreAction;
+ return operation == ARKUI_DROP_OPERATION_COPY ?
+ Qt::CopyAction : Qt::MoveAction;
+}
+
+QPoint QOhDragEvent::touchPoint() const
+{
+ return {static_cast<int>(OH_ArkUI_DragEvent_GetTouchPointXToWindow(m_dragEvent)),
+ static_cast<int>(OH_ArkUI_DragEvent_GetTouchPointYToWindow(m_dragEvent))};
+}
+
+void QOhDragEvent::failed()
+{
+ OH_ArkUI_DragEvent_SetDragResult(m_dragEvent, ARKUI_DRAG_RESULT_FAILED);
+}
+
+void QOhDragEvent::setSuggestedDropOperation(Qt::DropAction action)
+{
+ OH_ArkUI_DragEvent_SetSuggestedDropOperation(m_dragEvent, Qt::CopyAction == action ?
+ ::ArkUI_DropOperation::ARKUI_DROP_OPERATION_COPY :
+ ::ArkUI_DropOperation::ARKUI_DROP_OPERATION_MOVE);
+}
+
+void QOhDragEvent::setResult(bool isAccepted)
+{
+ OH_ArkUI_DragEvent_SetDragResult(m_dragEvent, isAccepted ? ARKUI_DRAG_RESULT_SUCCESSFUL :
+ ARKUI_DRAG_RESULT_CANCELED);
+}
+
+QStringList QOhDragEvent::types() const
+{
+ QStringList resultList;
+ int32_t typeCount(0);
+ int32_t result = OH_ArkUI_DragEvent_GetDataTypeCount(m_dragEvent, &typeCount);
+ if (ARKUI_ERROR_CODE_NO_ERROR != result) {
+ LOGE("call OH_ArkUI_DragEvent_GetDataTypeCount failed. error:[%{public}d]", result);
+ return resultList;
+ }
+
+ std::vector<std::unique_ptr<char[]>> buffers;
+ buffers.reserve(typeCount);
+ std::vector<char*> types;
+ types.reserve(typeCount);
+ for (int i = 0; i < typeCount; ++i) {
+ buffers.emplace_back(std::make_unique<char[]>(SCHAR_MAX));
+ types.push_back(buffers.back().get());
+ }
+
+ result = OH_ArkUI_DragEvent_GetDataTypes(m_dragEvent, types.data(), typeCount, SCHAR_MAX);
+ if (ARKUI_ERROR_CODE_NO_ERROR != result) {
+ LOGE("call OH_ArkUI_DragEvent_GetDataTypes failed. error:[%{public}d]", result);
+ return resultList;
+ }
+
+ for (int i = 0; i < typeCount; ++i) {
+ const QString ohType = QString::fromUtf8(types.at(i));
+ if ((ohType == QString::fromUtf8(UDMF_META_GENERAL_FILE)) ||
+ (ohType == QString::fromUtf8(UDMF_META_IMAGE)) ||
+ (ohType == QString::fromUtf8(UDMF_META_VIDEO)))
+ resultList << QStringLiteral("text/uri-list");
+ else if ((ohType == QString::fromUtf8(UDMF_META_TEXT)) ||
+ (ohType == QString::fromUtf8(UDMF_META_PLAIN_TEXT)) ||
+ (ohType == QString::fromUtf8(UDMF_META_SCRIPT)))
+ resultList << QStringLiteral("text/plain");
+ else if (ohType == QString::fromUtf8(UDMF_META_HTML))
+ resultList << QStringLiteral("text/html");
+ else if (ohType == QString::fromUtf8(UDMF_META_OPENHARMONY_PIXEL_MAP))
+ resultList << QStringLiteral("application/x-qt-image");
+ else
+ resultList << ohType;
+ }
+ return resultList;
+}
+
+void QOhDragEvent::acquireDatas(QMimeData * const mimeData)
+{
+ QOhObjectHolder<OH_UdmfData> data(OH_UdmfData_Create, OH_UdmfData_Destroy);
+ OH_UdmfData *nativeData = data.object();
+ if (nativeData == nullptr)
+ return;
+ int32_t result = ARKUI_ERROR_CODE_NO_ERROR;
+ result = OH_ArkUI_DragEvent_GetUdmfData(m_dragEvent, nativeData);
+ if (result != ARKUI_ERROR_CODE_NO_ERROR) {
+ LOGE("OH_ArkUI_DragEvent_GetUdmfData failed: %{public}d", result);
+ return;
+ }
+ data.take();
+ QtOh::UdmfHelper::acquireDatasFromUdmfToMime(nativeData, mimeData);
+}
+/* Qt 6 only modify */
+Qt::KeyboardModifiers QOhDragEvent::keyboardModifiers() const
+{
+ uint64_t keys = 0;
+ OH_ArkUI_DragEvent_GetModifierKeyStates(m_dragEvent, &keys);
+ return QOhKeys::ohKeys2Qt(keys);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,36 @@
+#ifndef QOHDRAGEVENT_H
+#define QOHDRAGEVENT_H
+
+#include <QtCore/qnamespace.h>
+#include <arkui/drag_and_drop.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qpoint.h>
+
+QT_BEGIN_NAMESPACE
+class QMimeData;
+class QOhDragEvent
+{
+public:
+ QOhDragEvent(ArkUI_DragEvent *event);
+
+ Qt::DropActions actions() const;
+
+ QPoint touchPoint() const;
+
+ void failed();
+
+ void setSuggestedDropOperation(Qt::DropAction action);
+
+ void setResult(bool isAccepted);
+
+ Qt::KeyboardModifiers keyboardModifiers() const; /* Qt 6 only modify */
+
+ QStringList types() const;
+ void acquireDatas(QMimeData * const mimeData);
+private:
+ ArkUI_DragEvent *m_dragEvent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHDRAGEVENT_H
new file mode 100644
@@ -0,0 +1,24 @@
+INCLUDEPATH += $$PWD
+
+LIBS += -lohinputmethod
+
+HEADERS += \
+ $$PWD/qohcontroller.h \
+ $$PWD/qohtexteditorproxy.h \
+ $$PWD/qohinputmethodproxy.h \
+ $$PWD/qohinputmethod.h \
+ $$PWD/qohattachoptions.h \
+ $$PWD/qohcursorinfo.h \
+ $$PWD/qohtextconfig.h \
+ $$PWD/qohtextavoidinfo.h
+
+SOURCES += \
+ $$PWD/qohcontroller.cpp \
+ $$PWD/qohtexteditorproxy.cpp \
+ $$PWD/qohinputmethodproxy.cpp \
+ $$PWD/qohinputmethod.cpp \
+ $$PWD/qohattachoptions.cpp \
+ $$PWD/qohcursorinfo.cpp \
+ $$PWD/qohtextconfig.cpp \
+ $$PWD/qohtextavoidinfo.cpp
+
new file mode 100644
@@ -0,0 +1,41 @@
+#include "qohattachoptions.h"
+#include "qohinputmethod.h"
+#include <inputmethod/inputmethod_types_capi.h>
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+
+QOhAttachOptions::QOhAttachOptions(bool showKeyboard
+#if OHOS_SDK_VERSION > 14
+ , RequestKeyboardReason reason
+#endif
+ )
+#if OHOS_SDK_VERSION > 14
+ : m_options(new QOhObjectHolder<InputMethod_AttachOptions>(OH_AttachOptions_CreateWithRequestKeyboardReason, OH_AttachOptions_Destroy, showKeyboard, (InputMethod_RequestKeyboardReason)reason))
+#else
+ : m_options(new QOhObjectHolder<InputMethod_AttachOptions>(OH_AttachOptions_Create, OH_AttachOptions_Destroy, showKeyboard))
+#endif
+{
+
+}
+
+bool QOhAttachOptions::isShowKeyboard() const
+{
+ if (native() == nullptr)
+ return false;
+ bool result = false;
+ auto code = OH_AttachOptions_IsShowKeyboard(native(), &result);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("OH_AttachOptions_IsShowKeyboard failed: ", code);
+ }
+ return result;
+}
+
+InputMethod_AttachOptions *QOhAttachOptions::native() const
+{
+ return m_options->object();
+}
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,31 @@
+#ifndef QOHATTACHOPTIONS_H
+#define QOHATTACHOPTIONS_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qscopedpointer.h>
+#include <inputmethod/inputmethod_attach_options_capi.h>
+#include "qohinputmethod.h"
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhAttachOptions
+{
+public:
+ QOhAttachOptions(bool showKeyboard = true
+ #if OHOS_SDK_VERSION > 14
+ , RequestKeyboardReason reason = RequestKeyboardReason::MOUSE
+ #endif
+ );
+
+ bool isShowKeyboard() const;
+
+ InputMethod_AttachOptions *native() const;
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_AttachOptions>> m_options;
+};
+
+};
+QT_END_NAMESPACE
+#endif // QOHATTACHOPTIONS_H
new file mode 100644
@@ -0,0 +1,77 @@
+#include "qohcontroller.h"
+#include "qohinputmethod.h"
+#include "qohattachoptions.h"
+#include "qohtexteditorproxy.h"
+#include "qohinputmethodproxy.h"
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod {
+
+QOhController::QOhController()
+ : m_textEditorProxy(nullptr)
+ , m_inputMethodProxy(nullptr)
+{
+
+}
+
+QOhController::~QOhController()
+{
+ if (isAttached())
+ detach();
+ if (m_textEditorProxy != nullptr) {
+ delete m_textEditorProxy;
+ m_textEditorProxy = nullptr;
+ }
+}
+
+bool QOhController::isAttached() const
+{
+ return m_inputMethodProxy != nullptr;
+}
+
+bool QOhController::attach(const QOhAttachOptions &options)
+{
+ if (isAttached()) {
+ return true;
+ }
+ InputMethod_TextEditorProxy *teProxy = textEditorProxy()->native();
+ InputMethod_InputMethodProxy *inputMethodProxy = nullptr;
+ auto code = OH_InputMethodController_Attach(teProxy, options.native(), &inputMethodProxy);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("Input method controller attach failed: ", code);
+ return false;
+ }
+ m_inputMethodProxy = new QOhInputMethodProxy(inputMethodProxy);
+ return true;
+}
+
+bool QOhController::detach()
+{
+ if (!isAttached())
+ return true;
+ auto code = OH_InputMethodController_Detach(m_inputMethodProxy->native());
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("Input method controller detach failed: ", code);
+ return false;
+ }
+ delete m_inputMethodProxy;
+ m_inputMethodProxy = nullptr;
+ return true;
+}
+
+QOhInputMethodProxy *QOhController::inputMethodProxy() const
+{
+ return m_inputMethodProxy;
+}
+
+QOhTextEditorProxy *QOhController::textEditorProxy() const
+{
+ if (m_textEditorProxy == nullptr)
+ m_textEditorProxy = new QOhTextEditorProxy();
+ return m_textEditorProxy;
+}
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,38 @@
+#ifndef QOHCONTROLLER_H
+#define QOHCONTROLLER_H
+
+#include <inputmethod/inputmethod_controller_capi.h>
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace InputMethod {
+class QOhTextEditorProxy;
+class QOhAttachOptions;
+class QOhInputMethodProxy;
+
+class QOhController
+{
+public:
+ QOhController();
+ ~QOhController();
+
+ bool attach(const QOhAttachOptions &options);
+
+ bool isAttached() const;
+
+ bool detach();
+
+ QOhInputMethodProxy *inputMethodProxy() const;
+
+ QOhTextEditorProxy *textEditorProxy() const;
+
+private:
+ mutable QOhTextEditorProxy *m_textEditorProxy;
+ QOhInputMethodProxy *m_inputMethodProxy;
+};
+
+};
+QT_END_NAMESPACE
+
+#endif // QOHCONTROLLER_H
new file mode 100644
@@ -0,0 +1,33 @@
+#include "qohcursorinfo.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+
+QOhCursorInfo::QOhCursorInfo(InputMethod_CursorInfo *info)
+{
+ m_cursorInfoObject.reset(new QOhObjectHolder<InputMethod_CursorInfo>(info));
+}
+
+QOhCursorInfo::QOhCursorInfo(double left, double top, double width, double height)
+{
+ m_cursorInfoObject.reset(new QOhObjectHolder<InputMethod_CursorInfo>(OH_CursorInfo_Create, OH_CursorInfo_Destroy, left, top, width, height));
+}
+
+QRectF QOhCursorInfo::boundingRect() const
+{
+ double left = 0;
+ double top = 0;
+ double width = 0;
+ double height = 0;
+ OH_CursorInfo_GetRect(m_cursorInfoObject->object(), &left, &top, &width, &height);
+ return QRectF(left, top, width, height);
+}
+
+InputMethod_CursorInfo *QOhCursorInfo::native() const
+{
+ return m_cursorInfoObject->object();
+}
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QOHCURSORINFO_H
+#define QOHCURSORINFO_H
+
+#include <QtCore/qglobal.h>
+#include <inputmethod/inputmethod_cursor_info_capi.h>
+#include <QtCore/qrect.h>
+#include <QScopedPointer>
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhCursorInfo
+{
+public:
+ QOhCursorInfo(InputMethod_CursorInfo *info);
+ QOhCursorInfo(double left, double top, double width, double height);
+ QRectF boundingRect() const;
+
+ InputMethod_CursorInfo *native() const;
+
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_CursorInfo>> m_cursorInfoObject;
+};
+
+};
+QT_END_NAMESPACE
+#endif // QOHCURSORINFO_H
new file mode 100644
@@ -0,0 +1,163 @@
+#include "qohinputmethod.h"
+
+#include <QHash>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static QHash<InputMethod_ErrorCode, QString> errors = {
+ {IME_ERR_OK, "Correct case"},
+ {IME_ERR_UNDEFINED, "Error is undefined"},
+ {IME_ERR_PARAMCHECK, "Parameter check failed"},
+ {IME_ERR_PACKAGEMANAGER, "The bundle manager error"},
+ {IME_ERR_IMENGINE, "Input method engine error"},
+ {IME_ERR_IMCLIENT, "Input method client error"},
+ {IME_ERR_CONFIG_PERSIST, "Configuration persistence error"},
+ {IME_ERR_CONTROLLER, "Input method controller error"},
+ {IME_ERR_SETTINGS, "Input method setting error"},
+ {IME_ERR_IMMS, "Input method manager service error"},
+ {IME_ERR_DETACHED, "Input method client detached"},
+ {IME_ERR_NULL_POINTER, "Unexpected null pointer"},
+ {IME_ERR_QUERY_FAILED, "Query failed"}
+};
+
+QString InputMethod::ErrorCode::toString(InputMethod_ErrorCode code)
+{
+ return errors.value(code, "Unknow error");
+}
+
+void InputMethod::ErrorCode::printErrorCode(const QString &message, InputMethod_ErrorCode code)
+{
+ qWarning() << QString("%1---code is: %2, error string is: %3").arg(message).arg(code).arg(toString(code));
+}
+
+
+/*!
+ * \brief Qt-鸿蒙平台下的EnterKey功能值映射
+ * \param type Qt下的EnterKey功能枚举值
+ * \return 返回Qt-鸿蒙的EnterKey功能映射值
+ */
+InputMethod_EnterKeyType InputMethod::ohEnterKeyType(Qt::EnterKeyType type)
+{
+ switch (type) {
+ case Qt::EnterKeyDone:
+ return IME_ENTER_KEY_DONE;
+ case Qt::EnterKeyDefault:
+ return IME_ENTER_KEY_NONE;
+ case Qt::EnterKeyReturn:
+ return IME_ENTER_KEY_NEWLINE;
+ case Qt::EnterKeyPrevious:
+ return IME_ENTER_KEY_PREVIOUS;
+ case Qt::EnterKeyGo:
+ return IME_ENTER_KEY_GO;
+ case Qt::EnterKeySend:
+ return IME_ENTER_KEY_SEND;
+ case Qt::EnterKeySearch:
+ return IME_ENTER_KEY_SEARCH;
+ case Qt::EnterKeyNext:
+ return IME_ENTER_KEY_NEXT;
+ default: break;
+ }
+ return IME_ENTER_KEY_UNSPECIFIED;
+}
+
+Qt::EnterKeyType InputMethod::qtEnterKeyType(InputMethod_EnterKeyType type)
+{
+ switch (type) {
+ case IME_ENTER_KEY_GO:
+ return Qt::EnterKeyGo;
+ case IME_ENTER_KEY_SEARCH:
+ return Qt::EnterKeySearch;
+ case IME_ENTER_KEY_PREVIOUS:
+ return Qt::EnterKeyPrevious;
+ case IME_ENTER_KEY_SEND:
+ return Qt::EnterKeySend;
+ case IME_ENTER_KEY_NEXT:
+ return Qt::EnterKeyNext;
+ case IME_ENTER_KEY_DONE:
+ return Qt::EnterKeyDone;
+ case IME_ENTER_KEY_NEWLINE:
+ return Qt::EnterKeyReturn;
+ default: break;
+ }
+ return Qt::EnterKeyDefault;
+}
+
+
+/*!
+ * \brief Qt-鸿蒙平台下的输入类型枚举值映射
+ * \param hints Qt下的枚举值
+ * \return 返回Qt-鸿蒙的输入类型映射值
+ */
+InputMethod_TextInputType InputMethod::ohTextInputType(Qt::InputMethodHints hints)
+{
+ /* FIXME Qt支持位运算,鸿蒙文本类型不支持位运算的枚举值 */
+ if(hints.testFlag(Qt::ImhMultiLine)) /* 多行文本类型 */
+ return IME_TEXT_INPUT_TYPE_MULTILINE;
+
+ if ((hints.testFlag(Qt::ImhDigitsOnly) ||
+ hints.testFlag(Qt::ImhFormattedNumbersOnly)) &&
+ hints.testFlag(Qt::ImhHiddenText)) /* 数字密码类型 */
+ return IME_TEXT_INPUT_TYPE_NUMBER_PASSWORD;
+ if (hints.testFlag(Qt::ImhPreferNumbers) ||
+ hints.testFlag(Qt::ImhDigitsOnly)) /* 数字类型 */
+ return IME_TEXT_INPUT_TYPE_NUMBER;
+
+ if (hints.testFlag(Qt::ImhDate) ||
+ hints.testFlag(Qt::ImhTime)) /* 日期类型 */
+ return IME_TEXT_INPUT_TYPE_DATETIME;
+
+ if (hints.testFlag(Qt::ImhHiddenText)) /* 密码类型 */
+ return IME_TEXT_INPUT_TYPE_VISIBLE_PASSWORD;
+
+ if (hints.testFlag(Qt::ImhDialableCharactersOnly)) /* 电话号码类型 */
+ return IME_TEXT_INPUT_TYPE_PHONE;
+
+ if (hints.testFlag(Qt::ImhEmailCharactersOnly)) /* 邮箱地址类型 */
+ return IME_TEXT_INPUT_TYPE_EMAIL_ADDRESS;
+
+ if (hints.testFlag(Qt::ImhUrlCharactersOnly)) /* 链接类型 */
+ return IME_TEXT_INPUT_TYPE_URL;
+
+ return IME_TEXT_INPUT_TYPE_TEXT; /* 默认文本类型 */
+}
+
+Qt::InputMethodHints InputMethod::qtInputMethodHints(InputMethod_TextInputType type)
+{
+ Qt::InputMethodHints result = Qt::ImhNone;
+ switch (type) {
+ case IME_TEXT_INPUT_TYPE_TEXT:
+ result = Qt::ImhNone;
+ break;
+ case IME_TEXT_INPUT_TYPE_MULTILINE:
+ result = Qt::ImhMultiLine;
+ break;
+ case IME_TEXT_INPUT_TYPE_NUMBER:
+ result = Qt::ImhDigitsOnly;
+ break;
+ case IME_TEXT_INPUT_TYPE_PHONE:
+ result = Qt::ImhDialableCharactersOnly;
+ break;
+ case IME_TEXT_INPUT_TYPE_DATETIME:
+ result = Qt::ImhTime | Qt::ImhDate;
+ break;
+ case IME_TEXT_INPUT_TYPE_EMAIL_ADDRESS:
+ result = Qt::ImhEmailCharactersOnly;
+ break;
+ case IME_TEXT_INPUT_TYPE_URL:
+ result = Qt::ImhUrlCharactersOnly;
+ break;
+ case IME_TEXT_INPUT_TYPE_VISIBLE_PASSWORD:
+ case IME_TEXT_INPUT_TYPE_NUMBER_PASSWORD:
+ case IME_TEXT_INPUT_TYPE_NEW_PASSWORD:
+ result = Qt::ImhHiddenText;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,38 @@
+#ifndef QOHINPUTMETHOD_H
+#define QOHINPUTMETHOD_H
+
+#include <inputmethod/inputmethod_types_capi.h>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+namespace InputMethod
+{
+namespace ErrorCode {
+
+QString toString(InputMethod_ErrorCode code);
+
+void printErrorCode(const QString &message, InputMethod_ErrorCode code);
+};
+
+InputMethod_EnterKeyType ohEnterKeyType(Qt::EnterKeyType type);
+Qt::EnterKeyType qtEnterKeyType(InputMethod_EnterKeyType type);
+
+InputMethod_TextInputType ohTextInputType(Qt::InputMethodHints hints);
+Qt::InputMethodHints qtInputMethodHints(InputMethod_TextInputType type);
+
+enum class RequestKeyboardReason {
+ NONE = 0,
+
+ MOUSE = 1,
+
+ TOUCH = 2,
+
+ OTHER = 20
+};
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHINPUTMETHOD_H
new file mode 100644
@@ -0,0 +1,95 @@
+#include "qohinputmethodproxy.h"
+#include "qohinputmethod.h"
+#include "qohcursorinfo.h"
+#if OHOS_SDK_VERSION > 14
+#include "qohattachoptions.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+
+QOhInputMethodProxy::QOhInputMethodProxy(InputMethod_InputMethodProxy *inputMethodProxy)
+ : m_inputMethodProxyObject(new QOhObjectHolder<InputMethod_InputMethodProxy>(inputMethodProxy))
+{
+
+}
+
+InputMethod_InputMethodProxy *QOhInputMethodProxy::native() const
+{
+ return m_inputMethodProxyObject->object();
+}
+
+bool QOhInputMethodProxy::notifyConfigurationChanged(Qt::EnterKeyType enterKeyType, Qt::InputMethodHints hints)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_InputMethodProxy_NotifyConfigurationChange(native(), InputMethod::ohEnterKeyType(enterKeyType),
+ InputMethod::ohTextInputType(hints));
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("notify configuration change failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhInputMethodProxy::notifySelectionChanged(const QString &text, int start, int end)
+{
+ if (native() == nullptr)
+ return false;
+ auto data = reinterpret_cast<char16_t *>(const_cast<ushort*>(text.utf16()));
+ auto code = OH_InputMethodProxy_NotifySelectionChange(native(), data,
+ text.length() * sizeof(char16_t), start, end);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("notify selection change failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhInputMethodProxy::notifyCursorUpdate(const QOhCursorInfo &info)
+{
+ auto native_info = info.native();
+ if (native() == nullptr || native_info == nullptr)
+ return false;
+ auto code = OH_InputMethodProxy_NotifyCursorUpdate(native(), native_info);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("notify selection change failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhInputMethodProxy::showKeyboard()
+{
+ auto code = OH_InputMethodProxy_ShowKeyboard(native());
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("show keyboard failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhInputMethodProxy::hideKeyboard()
+{
+ auto code = OH_InputMethodProxy_HideKeyboard(native());
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("hide keyboard failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+#if OHOS_SDK_VERSION > 14
+bool QOhInputMethodProxy::showTextInput(const QOhAttachOptions &options)
+{
+ auto code = OH_InputMethodProxy_ShowTextInput(native(), options.native());
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("show text input failed: ", code);
+ return false;
+ }
+ return true;
+}
+#endif
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,39 @@
+#ifndef QOHINPUTMETHODPROXY_H
+#define QOHINPUTMETHODPROXY_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qscopedpointer.h>
+#include <inputmethod/inputmethod_controller_capi.h>
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhCursorInfo;
+#if OHOS_SDK_VERSION > 14
+class QOhAttachOptions;
+#endif
+class QOhInputMethodProxy
+{
+public:
+ QOhInputMethodProxy(InputMethod_InputMethodProxy *inputMethodProxy);
+
+ InputMethod_InputMethodProxy *native() const;
+
+ bool notifyConfigurationChanged(Qt::EnterKeyType enterKeyType, Qt::InputMethodHints hints);
+
+ bool notifySelectionChanged(const QString &text, int start, int end);
+
+ bool notifyCursorUpdate(const QOhCursorInfo &info);
+ bool showKeyboard();
+ bool hideKeyboard();
+#if OHOS_SDK_VERSION > 14
+ bool showTextInput(const QOhAttachOptions &options);
+#endif
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_InputMethodProxy>> m_inputMethodProxyObject;
+};
+
+};
+QT_END_NAMESPACE
+#endif // QOHINPUTMETHODPROXY_H
new file mode 100644
@@ -0,0 +1,75 @@
+#include "qohtextavoidinfo.h"
+#include "qohinputmethod.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+
+QOhTextAvoidInfo::QOhTextAvoidInfo(InputMethod_TextAvoidInfo *info)
+ : m_textAvoidInfoObject(new QOhObjectHolder<InputMethod_TextAvoidInfo>(info))
+{
+
+}
+
+QOhTextAvoidInfo::QOhTextAvoidInfo(double positionY, double height)
+ : m_textAvoidInfoObject(new QOhObjectHolder<InputMethod_TextAvoidInfo>(OH_TextAvoidInfo_Create, OH_TextAvoidInfo_Destroy, positionY, height))
+{
+}
+
+InputMethod_TextAvoidInfo *QOhTextAvoidInfo::native() const
+{
+ return m_textAvoidInfoObject->object();
+}
+
+bool QOhTextAvoidInfo::setPositionY(double positionY)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextAvoidInfo_SetPositionY(native(), positionY);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set text avoid position y failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+double QOhTextAvoidInfo::positionY() const
+{
+ if (native() == nullptr)
+ return -1;
+ double _positionY = -1;
+ auto code = OH_TextAvoidInfo_GetPositionY(native(), &_positionY);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get text avoid position y failed: ", code);
+ return _positionY;
+ }
+ return _positionY;
+}
+
+bool QOhTextAvoidInfo::setHeight(double height)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextAvoidInfo_SetHeight(native(), height);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set text avoid height failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+double QOhTextAvoidInfo::height() const
+{
+ if (native() == nullptr)
+ return -1;
+ double _height = -1;
+ auto code = OH_TextAvoidInfo_GetHeight(native(), &_height);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get text avoid heigh failed: ", code);
+ return _height;
+ }
+ return _height;
+}
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,33 @@
+#ifndef QOHTEXTAVOIDINFO_H
+#define QOHTEXTAVOIDINFO_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qscopedpointer.h>
+#include <inputmethod/inputmethod_text_avoid_info_capi.h>
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhTextAvoidInfo
+{
+public:
+ QOhTextAvoidInfo(InputMethod_TextAvoidInfo *info);
+ QOhTextAvoidInfo(double positionY, double height);
+
+ InputMethod_TextAvoidInfo *native() const;
+
+ bool setPositionY(double positionY);
+ double positionY() const;
+
+ bool setHeight(double height);
+ double height() const;
+
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_TextAvoidInfo>> m_textAvoidInfoObject;
+};
+
+};
+QT_END_NAMESPACE
+#endif // QOHTEXTAVOIDINFO_H
new file mode 100644
@@ -0,0 +1,176 @@
+#include "qohtextconfig.h"
+#include "qohinputmethod.h"
+#include "qohcursorinfo.h"
+#include "qohtextavoidinfo.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+
+QOhTextConfig::QOhTextConfig(InputMethod_TextConfig *config)
+{
+ if (config == nullptr)
+ m_configObject.reset(new QOhObjectHolder<InputMethod_TextConfig>(OH_TextConfig_Create, OH_TextConfig_Destroy));
+ else
+ m_configObject.reset(new QOhObjectHolder<InputMethod_TextConfig>(config));
+}
+
+bool QOhTextConfig::setInputType(Qt::InputMethodHints hints)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextConfig_SetInputType(native(), InputMethod::ohTextInputType(hints));
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set config input type failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+Qt::InputMethodHints QOhTextConfig::inputType() const
+{
+ if (native() == nullptr)
+ return Qt::ImhNone;
+ InputMethod_TextInputType type = IME_TEXT_INPUT_TYPE_NONE;
+ auto code = OH_TextConfig_GetInputType(native(), &type);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get config input type failed: ", code);
+ return Qt::ImhNone;
+ }
+ return InputMethod::qtInputMethodHints(type);
+}
+
+bool QOhTextConfig::setEnterKeyType(Qt::EnterKeyType enterKeyType)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextConfig_SetEnterKeyType(native(), InputMethod::ohEnterKeyType(enterKeyType));
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set config enter key type failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+Qt::EnterKeyType QOhTextConfig::enterKeyType() const
+{
+ if (native() == nullptr)
+ return Qt::EnterKeyDefault;
+ InputMethod_EnterKeyType type = IME_ENTER_KEY_NONE;
+ auto code = OH_TextConfig_GetEnterKeyType(native(), &type);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get config enter key type failed: ", code);
+ return Qt::EnterKeyDefault;
+ }
+ return InputMethod::qtEnterKeyType(type);
+}
+
+bool QOhTextConfig::setPreviewTextSupport(bool support)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextConfig_SetPreviewTextSupport(native(), support);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set preview text support failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhTextConfig::isPreviewTextSupport() const
+{
+ if (native() == nullptr)
+ return false;
+ bool result = false;
+ auto code = OH_TextConfig_IsPreviewTextSupported(native(), &result);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get preview text support failed: ", code);
+ return false;
+ }
+ return result;
+}
+
+QOhCursorInfo *QOhTextConfig::cursorInfo() const
+{
+ if (native() == nullptr)
+ return nullptr;
+ InputMethod_CursorInfo *info = nullptr;
+ auto code = OH_TextConfig_GetCursorInfo(native(), &info);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get cursor info failed: ", code);
+ return nullptr;
+ }
+ return new QOhCursorInfo(info);
+}
+
+QOhTextAvoidInfo *QOhTextConfig::textAvoidInfo() const
+{
+ if (native() == nullptr)
+ return nullptr;
+ InputMethod_TextAvoidInfo *info = nullptr;
+ auto code = OH_TextConfig_GetTextAvoidInfo(native(), &info);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get text avoid info failed: ", code);
+ return nullptr;
+ }
+ return new QOhTextAvoidInfo(info);
+}
+
+bool QOhTextConfig::setSelection(int32_t start, int32_t end)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextConfig_SetSelection(native(), start, end);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set selection failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+std::pair<int32_t, int32_t> QOhTextConfig::selection() const
+{
+ if (native() == nullptr)
+ return {0, 0};
+ int32_t start = 0;
+ int32_t end = 0;
+ auto code = OH_TextConfig_GetSelection(native(), &start, &end);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get selection failed: ", code);
+ return {0, 0};
+ }
+ return { start, end };
+}
+
+bool QOhTextConfig::setWindowId(int32_t id)
+{
+ if (native() == nullptr)
+ return false;
+ auto code = OH_TextConfig_SetWindowId(native(), id);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("set window id failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+int32_t QOhTextConfig::windowId() const
+{
+ if (native() == nullptr)
+ return -1;
+ int32_t id = -1;
+ auto code = OH_TextConfig_GetWindowId(native(), &id);
+ if (code != IME_ERR_OK) {
+ InputMethod::ErrorCode::printErrorCode("get window id failed: ", code);
+ }
+ return id;
+}
+
+InputMethod_TextConfig *QOhTextConfig::native() const
+{
+ return m_configObject->object();
+}
+
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,48 @@
+#ifndef QOHTEXTCONFIG_H
+#define QOHTEXTCONFIG_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qscopedpointer.h>
+#include <inputmethod/inputmethod_text_config_capi.h>
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhCursorInfo;
+class QOhTextAvoidInfo;
+
+class QOhTextConfig
+{
+public:
+ QOhTextConfig(InputMethod_TextConfig *config = nullptr);
+
+ bool setInputType(Qt::InputMethodHints hints);
+ Qt::InputMethodHints inputType() const;
+
+ bool setEnterKeyType(Qt::EnterKeyType enterKeyType);
+ Qt::EnterKeyType enterKeyType() const;
+
+ bool setPreviewTextSupport(bool support);
+ bool isPreviewTextSupport() const;
+
+ QOhCursorInfo *cursorInfo() const;
+ QOhTextAvoidInfo *textAvoidInfo() const;
+
+ bool setSelection(int32_t start, int32_t end);
+ std::pair<int32_t, int32_t> selection() const;
+
+ bool setWindowId(int32_t id);
+ int32_t windowId() const;
+
+ InputMethod_TextConfig *native() const;
+
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_TextConfig>> m_configObject;
+};
+
+};
+
+QT_END_NAMESPACE
+#endif // QOHTEXTCONFIG_H
new file mode 100644
@@ -0,0 +1,254 @@
+#include "qohtexteditorproxy.h"
+#include <QHash>
+#include <QGuiApplication>
+#include <QInputMethodQueryEvent>
+#include "qohauxiliary.h"
+#include "qohtextconfig.h"
+
+QT_BEGIN_NAMESPACE
+
+static QHash<InputMethod_TextEditorProxy *, InputMethod::QOhTextEditorProxy *> g_editors;
+
+namespace InputMethod
+{
+
+QOhTextEditorProxy::QOhTextEditorProxy()
+{
+ create();
+}
+
+QOhTextEditorProxy::~QOhTextEditorProxy()
+{
+ destroy();
+}
+
+InputMethod_TextEditorProxy *QOhTextEditorProxy::native() const
+{
+ return m_textEditorProxyObject->object();
+}
+
+bool QOhTextEditorProxy::isKeyboardVisible() const
+{
+ return m_keyboardVisible;
+}
+
+bool QOhTextEditorProxy::create()
+{
+ m_textEditorProxyObject.reset(new QOhObjectHolder<InputMethod_TextEditorProxy>(OH_TextEditorProxy_Create, OH_TextEditorProxy_Destroy));
+ auto textEditorProxy = m_textEditorProxyObject->object();
+ if (textEditorProxy == nullptr)
+ return false;
+ g_editors.insert(textEditorProxy, this);
+ OH_TextEditorProxy_SetSendKeyboardStatusFunc(textEditorProxy, &QOhTextEditorProxy::keyboardStatusChangeCallback);
+ OH_TextEditorProxy_SetDeleteBackwardFunc(textEditorProxy, &QOhTextEditorProxy::deleteBackwardCallback);
+ OH_TextEditorProxy_SetDeleteForwardFunc(textEditorProxy, &QOhTextEditorProxy::deleteForwardCallback);
+ OH_TextEditorProxy_SetInsertTextFunc(textEditorProxy, &QOhTextEditorProxy::insertTextCallback);
+ OH_TextEditorProxy_SetMoveCursorFunc(textEditorProxy, &QOhTextEditorProxy::moveCursorCallback);
+ OH_TextEditorProxy_SetSetPreviewTextFunc(textEditorProxy, &QOhTextEditorProxy::setPreviewTextCallback);
+ OH_TextEditorProxy_SetGetTextConfigFunc(textEditorProxy, &QOhTextEditorProxy::getTextConfigCallback);
+ OH_TextEditorProxy_SetSendEnterKeyFunc(textEditorProxy, &QOhTextEditorProxy::sendEnterKeyCallback);
+ OH_TextEditorProxy_SetHandleSetSelectionFunc(textEditorProxy, &QOhTextEditorProxy::handleSetSelectionCallback);
+ OH_TextEditorProxy_SetHandleExtendActionFunc(textEditorProxy, &QOhTextEditorProxy::handleExtendActionCallback);
+ OH_TextEditorProxy_SetGetLeftTextOfCursorFunc(textEditorProxy, &QOhTextEditorProxy::getLeftTextOfCursorCallback);
+ OH_TextEditorProxy_SetGetRightTextOfCursorFunc(textEditorProxy, &QOhTextEditorProxy::getRightTextOfCursorCallback);
+ OH_TextEditorProxy_SetGetTextIndexAtCursorFunc(textEditorProxy, &QOhTextEditorProxy::getTextIndexAtCursorCallback);
+ OH_TextEditorProxy_SetReceivePrivateCommandFunc(textEditorProxy, &QOhTextEditorProxy::receivePrivateCommandCallback);
+ OH_TextEditorProxy_SetFinishTextPreviewFunc(textEditorProxy, &QOhTextEditorProxy::finishTextPreviewCallback);
+ return true;
+}
+
+void QOhTextEditorProxy::destroy()
+{
+ g_editors.remove(native());
+}
+
+void QOhTextEditorProxy::setKeyboardVisible(bool visible)
+{
+ if (m_keyboardVisible == visible)
+ return;
+ m_keyboardVisible = visible;
+ emit keyboardVisibleChanged();
+}
+
+void QOhTextEditorProxy::keyboardStatusChangeCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_KeyboardStatus keyboardStatus)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->setKeyboardVisible(keyboardStatus == IME_KEYBOARD_STATUS_SHOW);
+}
+
+void QOhTextEditorProxy::deleteBackwardCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t length)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->deleteBackward(length);
+}
+
+void QOhTextEditorProxy::deleteForwardCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t length)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->deleteForward(length);
+}
+
+void QOhTextEditorProxy::insertTextCallback(InputMethod_TextEditorProxy *textEditorProxy, const char16_t *text, size_t length)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->commitText(QString::fromUtf16(text, length));
+}
+
+void QOhTextEditorProxy::moveCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_Direction direction)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->moveCursor(direction);
+}
+
+void QOhTextEditorProxy::sendEnterKeyCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_EnterKeyType enterKeyType)
+{
+ Q_UNUSED(enterKeyType)
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->enterKeyDown();
+}
+
+int32_t QOhTextEditorProxy::setPreviewTextCallback(InputMethod_TextEditorProxy *textEditorProxy, const char16_t text[], size_t length, int32_t start, int32_t end)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return -1;
+ proxy->preeditText(QString::fromUtf16(text, length));
+ return 0;
+}
+
+void QOhTextEditorProxy::getTextConfigCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_TextConfig *config)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+
+ QOhTextConfig textConfig(config);
+ textConfig.setPreviewTextSupport(QtOh::isEnvironmentVariableIsTrue("QT_INPUTMETHOD_PREVIEW_TEXT_SUPPORT"));
+}
+
+void QOhTextEditorProxy::handleSetSelectionCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t start, int32_t end)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+}
+
+void QOhTextEditorProxy::handleExtendActionCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_ExtendAction action)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+}
+
+void QOhTextEditorProxy::getLeftTextOfCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t number, char16_t text[], size_t *length)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+
+ QString beforeText = QtOh::runOnQtMainThreadWithResult([] {
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return QString();
+ QInputMethodQueryEvent query(Qt::ImTextBeforeCursor);
+ QCoreApplication::sendEvent(input, &query);
+ QVariant result = query.value(Qt::ImTextBeforeCursor);
+ QString text;
+ if (result.isValid()) {
+ text = result.toString();
+ } else {
+ QInputMethodQueryEvent query =QInputMethodQueryEvent(Qt::ImCursorPosition | Qt::ImSurroundingText); /* Qt 6 only modify */
+ QCoreApplication::sendEvent(input, &query);
+ const int cursorPos = query.value(Qt::ImCursorPosition).toInt();
+ text = query.value(Qt::ImSurroundingText).toString().left(cursorPos);
+ }
+ return text;
+ });
+ if (beforeText.isEmpty())
+ return;
+ const char16_t *data = reinterpret_cast<const char16_t *>(beforeText.utf16());
+ size_t l = qMin(number, beforeText.length());
+ *length = l;
+ memcpy(text, data, l);
+ Q_EMIT proxy->updateCursor();
+}
+
+void QOhTextEditorProxy::getRightTextOfCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t number, char16_t text[], size_t *length)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+
+ QString afterText = QtOh::runOnQtMainThreadWithResult([] {
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return QString();
+ QInputMethodQueryEvent query(Qt::ImTextAfterCursor);
+ QCoreApplication::sendEvent(input, &query);
+ QVariant result = query.value(Qt::ImTextAfterCursor);
+ QString text;
+ if (result.isValid()) {
+ text = result.toString();
+ } else {
+ QInputMethodQueryEvent query = QInputMethodQueryEvent(Qt::ImCursorPosition | Qt::ImSurroundingText); /* Qt 6 only modify */
+ QCoreApplication::sendEvent(input, &query);
+ const int cursorPos = query.value(Qt::ImCursorPosition).toInt();
+ text = query.value(Qt::ImSurroundingText).toString().mid(cursorPos);
+ }
+ return text;
+ });
+ if (afterText.isEmpty())
+ return;
+ const char16_t *data = reinterpret_cast<const char16_t *>(afterText.utf16());
+ size_t l = qMin(number, afterText.length());
+ *length = l;
+ memcpy(text, data, l);
+}
+
+int32_t QOhTextEditorProxy::getTextIndexAtCursorCallback(InputMethod_TextEditorProxy *textEditorProxy)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return -1;
+ int32_t result = QtOh::runOnQtMainThreadWithResult([] {
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return 0;
+ QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText); /* Qt 6 only modify */
+ QCoreApplication::sendEvent(input, &query);
+ const int cursorPos = query.value(Qt::ImCursorPosition).toInt();
+ return cursorPos;
+ });
+ return result;
+}
+
+int32_t QOhTextEditorProxy::receivePrivateCommandCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_PrivateCommand *privateCommand[], size_t size)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return -1;
+ return 0;
+}
+
+void QOhTextEditorProxy::finishTextPreviewCallback(InputMethod_TextEditorProxy *textEditorProxy)
+{
+ QOhTextEditorProxy *proxy = g_editors.value(textEditorProxy);
+ if (proxy == nullptr)
+ return;
+ proxy->finishTextPreview();
+}
+
+};
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,60 @@
+#ifndef QOHTEXTEDITORPROXY_H
+#define QOHTEXTEDITORPROXY_H
+
+#include <QObject>
+#include <QScopedPointer>
+#include <inputmethod/inputmethod_text_editor_proxy_capi.h>
+#include "qohobjectholder.h"
+
+QT_BEGIN_NAMESPACE
+namespace InputMethod
+{
+class QOhTextEditorProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QOhTextEditorProxy();
+ ~QOhTextEditorProxy();
+
+ InputMethod_TextEditorProxy *native() const;
+
+ bool isKeyboardVisible() const;
+
+ bool create();
+ void destroy();
+
+signals:
+ void updateCursor();
+ void keyboardVisibleChanged();
+ void deleteBackward(int length);
+ void deleteForward(int length);
+ void commitText(const QString &text);
+ void preeditText(const QString &text);
+ void moveCursor(int direction);
+ void enterKeyDown();
+ void finishTextPreview();
+private:
+ void setKeyboardVisible(bool visible);
+ static void keyboardStatusChangeCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_KeyboardStatus keyboardStatus);
+ static void deleteBackwardCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t length);
+ static void deleteForwardCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t length);
+ static void insertTextCallback(InputMethod_TextEditorProxy *textEditorProxy, const char16_t *text, size_t length);
+ static void moveCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_Direction direction);
+ static void sendEnterKeyCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_EnterKeyType enterKeyType);
+ static int32_t setPreviewTextCallback(InputMethod_TextEditorProxy *textEditorProxy, const char16_t text[], size_t length, int32_t start, int32_t end);
+ static void getTextConfigCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_TextConfig *config);
+ static void handleSetSelectionCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t start, int32_t end);
+ static void handleExtendActionCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_ExtendAction action);
+ static void getLeftTextOfCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t number, char16_t text[], size_t *length);
+ static void getRightTextOfCursorCallback(InputMethod_TextEditorProxy *textEditorProxy, int32_t number, char16_t text[], size_t *length);
+ static int32_t getTextIndexAtCursorCallback(InputMethod_TextEditorProxy *textEditorProxy);
+ static int32_t receivePrivateCommandCallback(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_PrivateCommand *privateCommand[], size_t size);
+ static void finishTextPreviewCallback(InputMethod_TextEditorProxy *textEditorProxy);
+private:
+ QScopedPointer<QOhObjectHolder<InputMethod_TextEditorProxy>> m_textEditorProxyObject;
+ bool m_keyboardVisible = false;
+};
+
+};
+QT_END_NAMESPACE
+#endif // QOHTEXTEDITORPROXY_H
new file mode 100644
@@ -0,0 +1,47 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qjsability.h \
+ $$PWD/qjsabilityfactory.h \
+ $$PWD/qjsabilitystage.h \
+ $$PWD/qjsaccessmanager.h \
+ $$PWD/qjsdisplay.h \
+ $$PWD/qjsscreen.h \
+ $$PWD/qjsdeviceinfo.h \
+ $$PWD/qjsuiability.h \
+ $$PWD/qjsbasecontext.h \
+ $$PWD/qjscontext.h \
+ $$PWD/qjsapplicationcontext.h \
+ $$PWD/qjsuiabilitycontext.h \
+ $$PWD/qjsuiextensionability.h \
+ $$PWD/qjsuiextensioncontext.h \
+ $$PWD/qjsuiextensioncontextsession.h \
+ $$PWD/qjswant.h \
+ $$PWD/qjswindowproxy.h \
+ $$PWD/qjswindowstage.h \
+ $$PWD/qjscursor.h \
+ $$PWD/qjssettings.h \
+ $$PWD/qjsabilityinfo.h
+
+SOURCES += \
+ $$PWD/qjsability.cpp \
+ $$PWD/qjsabilityfactory.cpp \
+ $$PWD/qjsabilitystage.cpp \
+ $$PWD/qjsaccessmanager.cpp \
+ $$PWD/qjsdisplay.cpp \
+ $$PWD/qjsscreen.cpp \
+ $$PWD/qjsdeviceinfo.cpp \
+ $$PWD/qjsuiability.cpp \
+ $$PWD/qjsbasecontext.cpp \
+ $$PWD/qjscontext.cpp \
+ $$PWD/qjsapplicationcontext.cpp \
+ $$PWD/qjsuiabilitycontext.cpp \
+ $$PWD/qjsuiextensionability.cpp \
+ $$PWD/qjsuiextensioncontext.cpp \
+ $$PWD/qjsuiextensioncontextsession.cpp \
+ $$PWD/qjswant.cpp \
+ $$PWD/qjswindowproxy.cpp \
+ $$PWD/qjswindowstage.cpp \
+ $$PWD/qjscursor.cpp \
+ $$PWD/qjssettings.cpp \
+ $$PWD/qjsabilityinfo.cpp
new file mode 100644
@@ -0,0 +1,111 @@
+#include "qjswant.h"
+#include "qjsability.h"
+#include "qjscontext.h"
+#include "qjsuiability.h"
+#include "qohnativewindowmanager.h"
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsAbility::QJsAbility(Napi::Object jsUIAbility)
+ : QJsObject(jsUIAbility)
+ , m_context(nullptr)
+ , m_want(nullptr)
+ , m_canTerminateSelf(true)
+{
+ setOnDestroy();
+}
+
+QJsAbility::~QJsAbility()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if (!m_onDestroy.IsEmpty()) {
+ set("onDestroy", m_onDestroy.Value());
+ m_onDestroy.Reset();
+ }
+ });
+}
+
+QString QJsAbility::name() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ auto want = launchWant();
+ QString result;
+ if (want != nullptr) {
+ result = want->windowName();
+ }
+ if (result.isEmpty())
+ result = QJsWant::defaultWindowName;
+ return result;
+ });
+}
+
+Napi::Value QJsAbility::newLocalStorage() const
+{
+ return call("newLocalStorage");
+}
+
+QString QJsAbility::loadContentUrl() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ return QString::fromStdString(get("loadContentUrl").ToString());
+ });
+}
+
+QJsWant *QJsAbility::launchWant() const
+{
+ if (m_want == nullptr) {
+ m_want = QtOh::runOnJsUIThreadWithResult([this]{
+ return new QJsWant(get("launchWant").ToObject());
+ });
+ }
+ return m_want;
+}
+
+QByteArray QJsAbility::launchQtApplication() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ return QByteArray::fromStdString(get("launchApplication").ToString());
+ });
+}
+
+QList<QByteArray> QJsAbility::launchQtParams() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ QList<QByteArray> result;
+ QJsWant *want = launchWant();
+ if (want) {
+ result << want->launchParams();
+ }
+ result << QByteArray::fromStdString(get("launchParams").ToString()).split(' ');
+ return result;
+ });
+}
+
+void QJsAbility::terminateSelf()
+{
+ if (m_canTerminateSelf)
+ context()->terminateSelf();
+}
+
+void QJsAbility::setCanTerminateSelf(bool enable)
+{
+ m_canTerminateSelf = enable;
+}
+
+void QJsAbility::setOnDestroy()
+{
+ Napi::Function old = get("onDestroy").As<Napi::Function>();
+ m_onDestroy = Napi::Persistent(old);
+ Napi::Function onDestroy = QNapi::create([this](const Napi::CallbackInfo& info) {
+ if (m_onDestroy.IsEmpty())
+ return;
+ m_onDestroy.Call(info.This(), {});
+ m_onDestroy.Reset();
+
+ qNativeWindowManager->handleTopWindowDestroyed(this->name());
+ });
+ set("onDestroy", onDestroy);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,55 @@
+#ifndef QJSABILITY_H
+#define QJSABILITY_H
+
+#include "qjsobject.h"
+#include <QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+class QJsContext;
+class QJsWant;
+
+class QJsAbility : public QJsObject
+{
+public:
+ enum AbilityType
+ {
+ UIAbility,
+ UIExtensionAbility
+ };
+ QJsAbility(Napi::Object jsUIAbility);
+ virtual ~QJsAbility();
+
+ QString name() const;
+
+ virtual QJsContext *context() const = 0;
+
+ virtual Napi::Value newLocalStorage() const final;
+
+ QString loadContentUrl() const;
+
+ QJsWant *launchWant() const;
+
+ QByteArray launchQtApplication() const;
+
+ virtual QList<QByteArray> launchQtParams() const;
+
+ virtual AbilityType abilityType() const = 0;
+
+ void terminateSelf();
+
+ void setCanTerminateSelf(bool enable);
+
+protected:
+ void setOnDestroy();
+
+ mutable QJsContext *m_context;
+ mutable QJsWant *m_want;
+ bool m_canTerminateSelf;
+
+ Napi::FunctionReference m_onDestroy;
+ QAtomicInteger<bool> m_isAbilityDestryed;
+};
+
+QT_END_NAMESPACE
+#endif // QJSABILITY_H
new file mode 100644
@@ -0,0 +1,39 @@
+#include "qjsabilityfactory.h"
+#include "qjsuiability.h"
+#include "qjsuiextensionability.h"
+#include <QtCore/qnapi.h>
+#include <QtCore/QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+namespace QJsAbilityFactory {
+
+QJsAbility *create(const Napi::CallbackInfo& info)
+{
+ if (info.Length() == 0)
+ return nullptr;
+ auto ability = QNapi::getFirst<Napi::Object>(info);
+ if (ability.IsEmpty() || ability.IsUndefined())
+ return nullptr;
+
+ static QJsModule uiAbility("@ohos.app.ability.UIAbility");
+ auto prototypeOfUiAbility = uiAbility.get<Napi::Object>("prototype");
+ Napi::Function uiAbilityConstructor = prototypeOfUiAbility.Get("constructor").As<Napi::Function>();
+ if (ability.InstanceOf(uiAbilityConstructor))
+ return new QJsUIAbility(ability);
+
+ if (info.Length() < 2)
+ return nullptr;
+ auto session = info[1].As<Napi::Object>();
+ static QJsModule embeddedUIExtensionAbility("@ohos.app.ability.EmbeddedUIExtensionAbility");
+ auto prototypeOfEmbeddedUIExtensionAbility = embeddedUIExtensionAbility.get<Napi::Object>("prototype");
+ Napi::Function embeddedUIExtensionAbilityConstructor = prototypeOfEmbeddedUIExtensionAbility.Get("constructor").As<Napi::Function>();
+
+ if (ability.InstanceOf(embeddedUIExtensionAbilityConstructor))
+ return new QJsUIExtensionAbility(ability, session);
+ return nullptr;
+}
+
+}
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,13 @@
+#ifndef QJSABILITYFACTORY_H
+#define QJSABILITYFACTORY_H
+
+#include "qjsability.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QJsAbilityFactory {
+QJsAbility *create(const Napi::CallbackInfo &info);
+}
+
+QT_END_NAMESPACE
+#endif // QJSABILITYFACTORY_H
new file mode 100644
@@ -0,0 +1,45 @@
+#include "qjsabilityinfo.h"
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsAbilityInfo::QJsAbilityInfo(Napi::Object info)
+ : QJsObject(info)
+{
+
+}
+
+QHash<QString, QString> QJsAbilityInfo::metadata() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ QHash<QString, QString> result;
+ Napi::Array data = get("metadata").As<Napi::Array>();
+ for (uint32_t i = 0; i < data.Length(); ++i) {
+ Napi::Value keyValue = data[i];
+ Napi::Object keyValueObject = keyValue.ToObject();
+ QString key = QString::fromStdString(keyValueObject.Get("name").ToString());
+ QString value = QString::fromStdString(keyValueObject.Get("value").ToString());
+ result.insert(key, value);
+ }
+ return result;
+ });
+}
+
+bool QJsAbilityInfo::isInitGeometrySetted() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ static QStringList keys = {"ohos.ability.window.height", "ohos.ability.window.width",
+ "ohos.ability.window.left", "ohos.ability.window.right"};
+ Napi::Array data = get("metadata").As<Napi::Array>();
+ for (uint32_t i = 0; i < data.Length(); ++i) {
+ Napi::Value keyValue = data[i];
+ Napi::Object keyValueObject = keyValue.ToObject();
+ QString key = QString::fromStdString(keyValueObject.Get("name").ToString());
+ if (keys.contains(key))
+ return true;
+ }
+ return false;
+ });
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,20 @@
+#ifndef QJSABILITYINFO_H
+#define QJSABILITYINFO_H
+
+#include <QJsObject>
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+class QJsAbilityInfo : public QJsObject
+{
+public:
+ QJsAbilityInfo(Napi::Object info);
+
+ QHash<QString, QString> metadata() const;
+
+ bool isInitGeometrySetted() const;
+};
+
+QT_END_NAMESPACE
+#endif // QJSABILITYINFO_H
new file mode 100644
@@ -0,0 +1,174 @@
+#include <mutex>
+#include <qpa/qwindowsysteminterface.h>
+
+#include "qjswant.h"
+#include "qjsability.h"
+#include "qjsabilitystage.h"
+#include "private/qjsobject_p.h"
+#include "qopenharmonydefines.h"
+#include "qohnativewindowmanager.h"
+#include "private/qohhiappevent_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsAbilityStagePrivate : public QJsObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QJsAbilityStage)
+ Q_DISABLE_COPY_MOVE(QJsAbilityStagePrivate)
+
+public:
+ explicit QJsAbilityStagePrivate(QJsAbilityStage *const qq);
+ ~QJsAbilityStagePrivate();
+
+ static void attachAbilityStage(const Napi::CallbackInfo& info);
+
+ void setAcceptWant();
+ void setNewProcessRequest();
+
+private:
+ bool m_created;
+ Napi::FunctionReference m_funcRef;
+ Napi::FunctionReference m_onNewProcessRequest;
+ Napi::FunctionReference m_onAcceptWant;
+};
+
+QJsAbilityStagePrivate::QJsAbilityStagePrivate(QJsAbilityStage * const qq)
+ : QJsObjectPrivate(qq)
+ , m_created(false)
+{
+
+}
+
+QJsAbilityStagePrivate::~QJsAbilityStagePrivate()
+{
+ m_created = false;
+ m_funcRef.Reset();
+ m_onAcceptWant.Reset();
+ m_onNewProcessRequest.Reset();
+}
+
+void QJsAbilityStagePrivate::attachAbilityStage(const Napi::CallbackInfo &info)
+{
+ if (info.Length() < 1) {
+ LOGW("%{public}s param error", Q_FUNC_INFO);
+ return;
+ }
+
+ Napi::Object stage = info[0].As<Napi::Object>();
+ QJsAbilityStage::instance()->setObject(stage);
+ QJsAbilityStage::instance()->d_func()->m_created = true;
+ QJsAbilityStage::instance()->d_func()->setAcceptWant();
+ QJsAbilityStage::instance()->d_func()->setNewProcessRequest();
+
+#if OHOS_SDK_VERSION >= 15
+ Napi::Function prepareToTerminateAsync = Napi::Function::New(QJsAbilityStage::instance()->env(), [](const Napi::CallbackInfo& info){
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag("apt_begin");
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+ QWindowSystemInterface::handleApplicationTermination();
+ deferred.Resolve(Napi::Number::New(deferred.Env(), int(1)));
+ return deferred.Promise();
+ });
+ QJsAbilityStage::instance()->d_func()->m_funcRef = Napi::Persistent(prepareToTerminateAsync);
+ QJsAbilityStage::instance()->set("onPrepareTerminationAsync", prepareToTerminateAsync);
+#else
+ Q_UNUSED(info);
+ LOGW("support for api 15 and above is available.");
+#endif
+}
+
+void QJsAbilityStagePrivate::setAcceptWant()
+{
+ /*
+ * 返回ability的Key,如果已存在则直接拉起,否则创建新的ability。
+ * @param want
+ * @returns
+ */
+
+ Napi::Function old = QJsAbilityStage::instance()->get("onAcceptWant").As<Napi::Function>();
+ m_onAcceptWant = Napi::Persistent(old);
+ Napi::Function onAcceptWant = QNapi::create([this](const Napi::CallbackInfo& info){
+ std::vector<napi_value> args;
+ args.reserve(info.Length());
+ for (size_t i = 0; i < info.Length(); i++) {
+ args.push_back(info[i]);
+ }
+ auto result = m_onAcceptWant.Call(info.This(), args);
+ QString key = QNapi::get<QString>(result);
+ if (!key.isEmpty())
+ return result;
+ if (info.Length() < 1)
+ return QNapi::create(QJsWant::defaultWindowName);
+
+ static bool first = true;
+ QJsWant want(info[0].As<Napi::Object>());
+ if (!want.hasWindowName()) {
+ if (first) {
+ first = false;
+ key = QJsWant::defaultWindowName;
+ } else {
+ auto ability = qNativeWindowManager->defaultAbility();
+ if (ability == nullptr)
+ key = QJsWant::defaultWindowName;
+ else
+ key = ability->name();
+ }
+ } else {
+ key = want.windowName();
+ }
+ return QNapi::create(key);
+ });
+ QJsAbilityStage::instance()->set("onAcceptWant", onAcceptWant);
+}
+
+void QJsAbilityStagePrivate::setNewProcessRequest()
+{
+ auto old = QJsAbilityStage::instance()->get("onNewProcessRequest").As<Napi::Function>();
+ m_onNewProcessRequest = Napi::Persistent(old);
+ Napi::Function onNewProcessRequest = QNapi::create([this](const Napi::CallbackInfo& info){
+ QJsWant want(info[0].As<Napi::Object>());
+ if (!want.hasProcessIdentifier())
+ return m_onNewProcessRequest.Call(info.This(), {info[0]});
+ QString processIdentifier = want.processIdentifier();
+ return QNapi::create(processIdentifier);
+ });
+ QJsAbilityStage::instance()->set("onNewProcessRequest", onNewProcessRequest);
+}
+
+QJsAbilityStage::QJsAbilityStage()
+ : QJsObject(*(new QJsAbilityStagePrivate(this)))
+{
+
+}
+
+QJsAbilityStage::~QJsAbilityStage()
+{
+
+}
+
+bool QJsAbilityStage::isCreated() const
+{
+ Q_D(const QJsAbilityStage);
+ return d->m_created;
+}
+
+QJsAbilityStage *QJsAbilityStage::instance()
+{
+ static std::once_flag once;
+ std::call_once(once, []{
+ m_self.reset(new QJsAbilityStage());
+ });
+
+ return m_self.get();
+}
+
+void QJsAbilityStage::init(Napi::Env env, Napi::Object exports)
+{
+ static bool inited = false;
+ if (!inited) {
+ exports.Set(Napi::String::New(env, "attachAbilityStage"), Napi::Function::New(env, QJsAbilityStagePrivate::attachAbilityStage));
+ inited = true;
+ }
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,26 @@
+#ifndef QJSABILITYSTAGE_H
+#define QJSABILITYSTAGE_H
+
+#include "qjsobject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsAbilityStagePrivate;
+class QJsAbilityStage : public QJsObject
+{
+ Q_DECLARE_PRIVATE(QJsAbilityStage)
+ Q_DISABLE_COPY_MOVE(QJsAbilityStage)
+
+ QJsAbilityStage();
+ inline static QScopedPointer<QJsAbilityStage> m_self { nullptr };
+
+public:
+ ~QJsAbilityStage();
+ bool isCreated() const;
+
+ static QJsAbilityStage *instance();
+ static void init(Napi::Env env, Napi::Object exports);
+};
+
+QT_END_NAMESPACE
+#endif // QJSABILITYSTAGE_H
new file mode 100644
@@ -0,0 +1,240 @@
+#include <mutex>
+#include <QDebug>
+#include <QObject>
+#include <QThread>
+#include <QJsModule>
+#include <QJsObject>
+#include <QGuiApplication>
+
+#include "private/qopenharmony_p.h"
+#include "qjscontext.h"
+#include "qjsaccessmanager.h"
+#include "qohnativewindowmanager.h"
+#include <private/qjspromise_p.h>
+QT_BEGIN_NAMESPACE
+
+static std::once_flag sg_once;
+static QScopedPointer<QJsAccessManager> sg_self(Q_NULLPTR);
+
+class PermissionsResultClass : public QObject
+{
+ Q_OBJECT
+
+public:
+ PermissionsResultClass(const QtOhPrivate::PermissionsResultFunc &func) : m_func(func) {}
+ Q_INVOKABLE void sendResult(const QtOhPrivate::PermissionRequestResult &result) { m_func(result); delete this;}
+
+private:
+ QtOhPrivate::PermissionsResultFunc m_func;
+};
+
+class NotificationResultClass : public QObject
+{
+ Q_OBJECT
+
+public:
+ NotificationResultClass(const QtOhPrivate::enableNotificationFunc &func) : m_func(func) {}
+ Q_INVOKABLE void sendResult(bool result) { m_func(result); delete this;}
+
+private:
+ QtOhPrivate::enableNotificationFunc m_func;
+};
+
+QJsAccessManager::QJsAccessManager()
+{
+
+}
+
+QJsAccessManager *QJsAccessManager::instance()
+{
+ std::call_once(sg_once, []{
+ sg_self.reset(new QJsAccessManager());
+ });
+ return sg_self.data();
+}
+
+static void sendFailureRequestResults(const QStringList &permissions, PermissionsResultClass *result)
+{
+ QtOhPrivate::PermissionRequestResult requestResult;
+ requestResult.permissions.append(permissions);
+ for (int i = 0; i < permissions.length(); ++i) {
+ requestResult.authResults.append(QtOhPrivate::PermissionsResult::Denied);
+ requestResult.dialogShownResults.append(false);
+ }
+ Qt::ConnectionType connection = QThread::currentThread() == result->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
+ QMetaObject::invokeMethod(result, "sendResult", connection, Q_ARG(QtOhPrivate::PermissionRequestResult, requestResult));
+}
+
+static void sendResult(bool res, NotificationResultClass *result){
+ Qt::ConnectionType connection = QThread::currentThread() == result->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
+ QMetaObject::invokeMethod(result, "sendResult", connection, Q_ARG(bool, res));
+}
+
+void QJsAccessManager::requestPermissions(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc)
+{
+ PermissionsResultClass *result = new PermissionsResultClass(callbackFunc);
+ QJsObject *atManager = atManagerAcquire();
+ if (nullptr == atManager) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ sendFailureRequestResults(permissions, result);
+ return;
+ }
+
+ QtOh::runOnJsUIThreadNoWait([=]{
+ Napi::Array ps = Napi::Array::New(atManager->env(), permissions.length());
+ for (int i = 0; i < permissions.length(); ++i) {
+ ps.Set(i, permissions.at(i).toStdString());
+ }
+
+ QJsContext *context = qNativeWindowManager->context(qApp->focusWindow());
+ QJsPromise promise(atManager->call("requestPermissionsFromUser", { context->object(), ps }).As<Napi::Promise>());
+ promise.onThen([result](const Napi::CallbackInfo &info) {
+ Napi::Object data = info[0].As<Napi::Object>();
+ Napi::Array rps = data.Get("permissions").As<Napi::Array>();
+ Napi::Array auths = data.Get("authResults").As<Napi::Array>();
+ Napi::Array dialogShownResults = data.Get("dialogShownResults").As<Napi::Array>();
+
+ QtOhPrivate::PermissionRequestResult requestResult;
+ for (uint32_t i = 0; i < rps.Length(); ++i) {
+ Napi::Value tmpArg1 = rps[i];
+ Napi::Value tmpArg2 = auths[i];
+ Napi::Value tmpArg3 = dialogShownResults[i];
+ requestResult.permissions.append(QString::fromStdString(tmpArg1.ToString()));
+ requestResult.authResults.append((QtOhPrivate::PermissionsResult)(int32_t(tmpArg2.ToNumber())));
+ requestResult.dialogShownResults.append(tmpArg3.ToBoolean());
+ }
+ Qt::ConnectionType connection = QThread::currentThread() == result->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
+ QMetaObject::invokeMethod(result, "sendResult", connection, Q_ARG(QtOhPrivate::PermissionRequestResult, requestResult));
+ }).onCatch([=](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info);
+ sendFailureRequestResults(permissions, result);
+ });
+ });
+}
+
+void QJsAccessManager::requestPermissionsOnSetting(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc)
+{
+ PermissionsResultClass *result = new PermissionsResultClass(callbackFunc);
+ QJsObject *atManager = atManagerAcquire();
+ if (nullptr == atManager) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ sendFailureRequestResults(permissions, result);
+ return;
+ }
+
+ QtOh::runOnJsUIThreadNoWait([=]{
+ Napi::Array ps = Napi::Array::New(atManager->env(), permissions.length());
+ for (int i = 0; i < permissions.length(); ++i) {
+ ps.Set(i, permissions.at(i).toStdString());
+ }
+
+ QJsContext *context = qNativeWindowManager->context(qApp->focusWindow());
+
+ QJsPromise promise(atManager->call("requestPermissionOnSetting", { context->object(), ps }).As<Napi::Promise>());
+ promise.onThen([permissions, result](const Napi::CallbackInfo &info) {
+ Napi::Array grantStatus = info[0].As<Napi::Array>();
+ QtOhPrivate::PermissionRequestResult requestResult;
+ for (uint32_t i = 0; i < grantStatus.Length(); ++i) {
+ Napi::Value tmpArg1 = grantStatus[i];
+ requestResult.permissions.append(permissions.at(i));
+ requestResult.authResults.append((QtOhPrivate::PermissionsResult)(int32_t(tmpArg1.ToNumber())));
+ requestResult.dialogShownResults.append(false);
+ }
+ Qt::ConnectionType connection = QThread::currentThread() == result->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
+ QMetaObject::invokeMethod(result, "sendResult", connection, Q_ARG(QtOhPrivate::PermissionRequestResult, requestResult));
+ }).onCatch([=](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info);
+ sendFailureRequestResults(permissions, result);
+ });
+ });
+}
+
+void QJsAccessManager::requestEnableNotification(const QtOhPrivate::enableNotificationFunc &callbackFunc)
+{
+ NotificationResultClass *result = new NotificationResultClass(callbackFunc);
+
+ QJsObject *notifyMgr = notificationManagerAcquire();
+ if (nullptr == notifyMgr) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ sendResult(false, result);
+ return;
+ }
+
+ QtOh::runOnJsUIThreadNoWait([=]{
+ QJsContext *context = qNativeWindowManager->context(qApp->focusWindow());
+ QJsPromise promise(notifyMgr->call("requestEnableNotification", { context->object() }).As<Napi::Promise>());
+ promise.onThen([result](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info);
+ sendResult(true, result);
+ }).onCatch([result](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info);
+ sendResult(false, result);
+ });
+ });
+}
+
+bool QJsAccessManager::isNotificationEnabled()
+{
+ QJsObject *notifyMgr = notificationManagerAcquire();
+ if (nullptr == notifyMgr) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ return false;
+ }
+
+ return QtOh::runOnJsUIThreadWithResult([=]{
+ return notifyMgr->call<bool>("isNotificationEnabledSync");
+ });
+}
+
+void QJsAccessManager::openNotificationSettings()
+{
+ QJsObject *notifyMgr = notificationManagerAcquire();
+ if (nullptr == notifyMgr) {
+ qWarning() << Q_FUNC_INFO << "abilityAccessCtrl acquire failed.";
+ return;
+ }
+
+ QtOh::runOnJsUIThreadNoWait([=]{
+ QJsContext *context = qNativeWindowManager->context(qApp->focusWindow());
+ notifyMgr->call("openNotificationSettings", { context->object() });
+ });
+}
+
+QJsObject *QJsAccessManager::atManagerAcquire()
+{
+ if (m_atManager.isNull()) {
+ QtOh::runOnJsUIThreadAndWait([this]{
+ QJsModule atModule("@ohos.abilityAccessCtrl");
+ Napi::Object atMgrObject = atModule.call("createAtManager").As<Napi::Object>();
+ if (!atMgrObject.IsUndefined())
+ m_atManager.reset(new QJsObject(atMgrObject));
+ });
+ }
+
+ return m_atManager.data();
+}
+
+QJsObject *QJsAccessManager::bundleManagerAcquire()
+{
+ if (m_bundleMgr.isNull()) {
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_bundleMgr.reset(new QJsModule("@ohos.bundle.bundleManager"));
+ });
+ }
+ return m_bundleMgr.data();
+}
+
+QJsObject *QJsAccessManager::notificationManagerAcquire()
+{
+ if (m_notificationMgr.isNull()) {
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_notificationMgr.reset(new QJsModule("@ohos.notificationManager"));
+ });
+ }
+ return m_notificationMgr.data();
+}
+
+QT_END_NAMESPACE
+
+#include "qjsaccessmanager.moc"
+
new file mode 100644
@@ -0,0 +1,33 @@
+#ifndef QJSACCESSMANAGER_H
+#define QJSACCESSMANAGER_H
+
+#include <QScopedPointer>
+#include <QtGui/qpa/qplatformaccessctrl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QJsAccessManager : public QPlatformAccessCtrl
+{
+ explicit QJsAccessManager();
+
+public:
+ static QJsAccessManager *instance();
+ void requestPermissions(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc) override;
+ void requestPermissionsOnSetting(const QStringList &permissions, const QtOhPrivate::PermissionsResultFunc &callbackFunc) override;
+
+ void requestEnableNotification(const QtOhPrivate::enableNotificationFunc &callbackFunc) override;
+ bool isNotificationEnabled() override;
+ void openNotificationSettings() override;
+protected:
+ QJsObject *atManagerAcquire() override;
+ QJsObject *bundleManagerAcquire() override;
+ QJsObject *notificationManagerAcquire() override;
+
+private:
+ QScopedPointer<QJsObject> m_atManager;
+ QScopedPointer<QJsObject> m_bundleMgr;
+ QScopedPointer<QJsObject> m_notificationMgr;
+};
+
+QT_END_NAMESPACE
+#endif // QJSACCESSMANAGER_H
new file mode 100644
@@ -0,0 +1,21 @@
+#include "qjsapplicationcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsApplicationContext::QJsApplicationContext(Napi::Object jsObject)
+ : QJsContext(jsObject)
+{
+
+}
+
+QJsApplicationContext::~QJsApplicationContext()
+{
+
+}
+
+QJsContext::ContextType QJsApplicationContext::contextType() const
+{
+ return ContextType::ApplicationContext;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,18 @@
+#ifndef QJSAPPLICATIONCONTEXT_H
+#define QJSAPPLICATIONCONTEXT_H
+
+#include "qjscontext.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsApplicationContext : public QJsContext
+{
+public:
+ QJsApplicationContext(Napi::Object jsObject);
+ ~QJsApplicationContext();
+
+ ContextType contextType() const override;
+};
+
+QT_END_NAMESPACE
+#endif // QJSAPPLICATIONCONTEXT_H
new file mode 100644
@@ -0,0 +1,21 @@
+#include "qjsbasecontext.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsBaseContext::QJsBaseContext(Napi::Object jsObject)
+ : QJsObject(jsObject)
+{
+
+}
+
+QJsBaseContext::~QJsBaseContext()
+{
+
+}
+
+bool QJsBaseContext::stageMode() const
+{
+ return get("stageMode").ToBoolean();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,18 @@
+#ifndef QJSBASECONTEXT_H
+#define QJSBASECONTEXT_H
+
+#include "qjsobject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsBaseContext : public QJsObject
+{
+public:
+ QJsBaseContext(Napi::Object jsObject);
+ virtual ~QJsBaseContext();
+
+ bool stageMode() const;
+};
+
+QT_END_NAMESPACE
+#endif // QJSBASECONTEXT_H
new file mode 100644
@@ -0,0 +1,215 @@
+#include <rawfile/raw_file_manager.h>
+
+#include <qnapi.h>
+#include "qjscontext.h"
+#include "qjswindowstage.h"
+#include "qjsapplicationcontext.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsContext::QJsContext(Napi::Object jsObject)
+ : QJsBaseContext(jsObject)
+ , m_windowStage(nullptr)
+ , m_resourceManager(nullptr)
+ , m_context(nullptr)
+{
+
+}
+
+QJsContext::~QJsContext()
+{
+ if (m_windowStage != nullptr)
+ delete m_windowStage;
+ if (m_resourceManager != nullptr)
+ OH_ResourceManager_ReleaseNativeResourceManager(m_resourceManager);
+ if (m_context != nullptr)
+ delete m_context;
+}
+
+int QJsContext::colorMode() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ Napi::Object config = get<Napi::Object>("config");
+ return QNapi::get<int>(config, "colorMode");
+ });
+}
+
+QString QJsContext::cacheDir() const
+{
+ return get<QString>("cacheDir");
+}
+
+QString QJsContext::tempDir() const
+{
+ return get<QString>("tempDir");
+}
+
+QString QJsContext::filesDir() const
+{
+ return get<QString>("filesDir");
+}
+
+QString QJsContext::databaseDir() const
+{
+ return get<QString>("databaseDir");
+}
+
+QString QJsContext::preferencesDir() const
+{
+ return get<QString>("preferencesDir");
+}
+
+QString QJsContext::bundleCodeDir() const
+{
+ return get<QString>("bundleCodeDir");
+}
+
+QString QJsContext::distributedFilesDir() const
+{
+ return get<QString>("distributedFilesDir");
+}
+
+QString QJsContext::resourceDir() const
+{
+ return get<QString>("resourceDir");
+}
+
+QString QJsContext::cloudFileDir() const
+{
+ return get<QString>("cloudFileDir");
+}
+
+QString QJsContext::qmlDir() const
+{
+ return resourceDir() + "/qml";
+}
+
+QJsContext *QJsContext::applicationContext() const
+{
+ if (m_context == nullptr)
+ m_context = new QJsApplicationContext(call("getApplicationContext").ToObject());
+ return m_context;
+}
+
+QJsWindowStage *QJsContext::windowStage() const
+{
+ if (m_windowStage == nullptr) {
+ m_windowStage = QtOh::runOnJsUIThreadWithResult([this]{
+ return new QJsWindowStage(get("windowStage").ToObject());
+ });
+ }
+ return m_windowStage;
+}
+
+NativeResourceManager *QJsContext::resourceManager() const
+{
+ if (m_resourceManager == nullptr){
+ m_resourceManager = QtOh::runOnJsUIThreadWithResult([this]{
+ return OH_ResourceManager_InitNativeResourceManager(env(), get("resourceManager"));
+ });
+ }
+ return m_resourceManager;
+}
+
+static Napi::Value makeWant(const Want &want)
+{
+ auto d = QVariantMap{
+ {"bundleName", want.bundleName},
+ {"moduleName", want.moduleName},
+ {"abilityName", want.abilityName},
+ };
+ d.insert("parameters", QVariantMap{
+ {"windowName", want.parameters.windowName},
+ {"instanceKey", want.parameters.instanceKey}
+ }
+ );
+ Napi::Value jsWant = QNapi::create(d);
+ return jsWant;
+}
+
+void QJsContext::startAbility(const Want &want, const StartOptions &options, const Napi::Function &callback)
+{
+ Napi::Value jsWant = makeWant(want);
+ Napi::Value jsOptions = QNapi::create(QVariantMap{
+ {"windowLeft", options.windowLeft},
+ {"windowTop", options.windowTop},
+ {"windowWidth", options.windowWidth},
+ {"windowHeight", options.windowHeight},
+ {"withAnimation", options.withAnimation},
+ {"displayId", options.displayId}
+ });
+
+ call("startAbility", { jsWant, jsOptions, callback });
+}
+
+void QJsContext::startAbility(const Want &want, const Napi::Function &callback)
+{
+ call("startAbility", { makeWant(want), callback });
+}
+
+void QJsContext::startAbility(const Napi::Value &want)
+{
+ call("startAbility", { want });
+}
+
+void QJsContext::startAbility(const Napi::Value &want, const Napi::Function &callback)
+{
+ call("startAbility", { want, callback });
+}
+
+void QJsContext::startAbility(const Napi::Value &want, const Napi::Value &options, const Napi::Function &callback)
+{
+ call("startAbility", { want, options, callback });
+}
+
+void QJsContext::startAbilityForResult(const Napi::Value &want, const Napi::Value &options,const Napi::Function &callback)
+{
+ call("startAbilityForResult", { want, options, callback });
+}
+
+void QJsContext::terminateSelfWithResult(const Napi::Object &abilityResult)
+{
+ call("terminateSelfWithResult", { abilityResult });
+}
+
+bool QJsContext::showAbility()
+{
+ return QtOh::runOnJsUIThreadWithPromise<bool>([this](auto p){
+ QJsPromise promise(call("showAbility").As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info)
+ p->set_value(true);
+ }).onCatch([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info)
+ LOGW("call showAbility failed.");
+ p->set_value(false);
+ });
+ });
+}
+
+bool QJsContext::hideAbility()
+{
+ return QtOh::runOnJsUIThreadWithPromise<bool>([this](auto p){
+ QJsPromise promise(call("hideAbility").As<Napi::Promise>());
+
+ promise.onThen([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info)
+ p->set_value(true);
+ }).onCatch([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info)
+ LOGW("call hideAbility failed.");
+ p->set_value(false);
+ });
+ });
+}
+
+void QJsContext::terminateSelf()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ call("terminateSelf");
+ });
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,99 @@
+#ifndef QJSCONTEXT_H
+#define QJSCONTEXT_H
+
+#include <QString>
+
+#include "qjsbasecontext.h"
+
+QT_BEGIN_NAMESPACE
+
+struct Want
+{
+ QString bundleName;
+ QString moduleName;
+ QString abilityName;
+ struct Parameters {
+ QString windowName;
+ QString instanceKey;
+ } parameters;
+};
+
+struct StartOptions
+{
+ int windowMode;
+ int windowLeft;
+ int windowTop;
+ int windowWidth;
+ int windowHeight;
+ uint32_t displayId;
+ bool withAnimation;
+};
+
+class QJsWindowStage;
+class QJsApplicationContext;
+struct NativeResourceManager;
+class QJsContext : public QJsBaseContext
+{
+public:
+ enum ContextType
+ {
+ UIContext,
+ UIExtensionContext,
+ ApplicationContext
+ };
+ QJsContext(Napi::Object jsObject);
+ ~QJsContext();
+
+ int colorMode() const;
+
+ QString cacheDir() const;
+
+ QString tempDir() const;
+
+ QString filesDir() const;
+
+ QString databaseDir() const;
+
+ QString preferencesDir() const;
+
+ QString bundleCodeDir() const;
+
+ QString distributedFilesDir() const;
+
+ QString resourceDir() const;
+
+ QString cloudFileDir() const;
+
+ QString qmlDir() const;
+
+ QJsContext *applicationContext() const;
+
+ QJsWindowStage *windowStage() const;
+
+ NativeResourceManager *resourceManager() const;
+ void startAbility(const Want &want, const Napi::Function &callback = Napi::Function());
+
+ virtual ContextType contextType() const = 0;
+
+ void startAbility(const Want &want, const StartOptions &options, const Napi::Function &callback = Napi::Function());
+
+ void startAbility(const Napi::Value &want);
+ void startAbility(const Napi::Value &want, const Napi::Function &callback);
+ void startAbility(const Napi::Value &want, const Napi::Value &options, const Napi::Function &callback);
+
+ void startAbilityForResult(const Napi::Value &want, const Napi::Value &options = Napi::Value(),const Napi::Function &callback = Napi::Function());
+
+ void terminateSelfWithResult(const Napi::Object &abilityResult);
+
+ bool showAbility();
+ bool hideAbility();
+
+ void terminateSelf();
+private:
+ mutable QJsWindowStage *m_windowStage;
+ mutable NativeResourceManager *m_resourceManager;
+ mutable QJsApplicationContext *m_context;
+};
+
+QT_END_NAMESPACE
+#endif // QJSCONTEXT_H
new file mode 100644
@@ -0,0 +1,87 @@
+#include <QDebug>
+#include <QThread>
+#include <private/qopenharmony_p.h>
+#include <QCoreApplication>
+#include <multimedia/image_framework/image/pixelmap_native.h>
+
+#include "qjscursor.h"
+#include "qohauxiliary.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsCursor::QJsCursor()
+ : QJsModule("@ohos.multimodalInput.pointer")
+{
+
+}
+
+QJsCursor::~QJsCursor()
+{
+
+}
+
+bool QJsCursor::isVisible() const
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ return call<bool>("isPointerVisibleSync");
+ });
+}
+
+void QJsCursor::setVisible(bool visible)
+{
+ QtOh::runOnJsUIThreadAndWait([this, visible]{
+ call("setPointerVisible", { Napi::Value::From(env(), visible)} );
+ });
+}
+
+void QJsCursor::setCursor(int windowId, QCursorInfo *info)
+{
+ if (info == nullptr)
+ return;
+
+ if (info->shape == HIDE) {
+ setVisible(false);
+ return;
+ } else {
+ setVisible(true);
+ }
+
+ // if (!isVisible()) {
+ // setVisible(true);
+ // }
+
+ if (info->shape != CUSTOM) {
+ QtOh::runOnJsUIThreadAndWait([this, windowId, info]{
+ call("setPointerStyleSync", {Napi::Number::New(env(), windowId), Napi::Number::New(env(), info->shape)});
+ });
+ } else {
+ QtOh::runOnJsUIThreadAndWait([this, windowId, info]{
+ OH_PixelmapNative *nativePixel = QtOh::UdmfHelper::createNativePixelMapFromQImage(info->pixMap.toImage());
+ napi_value pixelMap { nullptr };
+ int result = ::OH_PixelmapNative_ConvertPixelmapNativeToNapi(env(), nativePixel, &pixelMap);
+ if (Image_ErrorCode::IMAGE_SUCCESS != result) {
+ ::OH_PixelmapNative_Release(nativePixel);
+ qWarning("%s convert cursor pixmap failed. code: %d", Q_FUNC_INFO, result);
+ return;
+ }
+
+ if (QtOh::apiVersion() >= 15) {
+ Napi::Object cursor = Napi::Object::New(env());
+ cursor.Set("pixelMap", Napi::Value::From(env(), pixelMap));
+ cursor.Set("focusX", Napi::Number::New(env(), info->focusX));
+ cursor.Set("focusY", Napi::Number::New(env(), info->focusY));
+
+ Napi::Object config = Napi::Object::New(env());
+ config.Set("followSystem", Napi::Boolean::New(env(), false));
+
+ call("setCustomCursor", { Napi::Number::New(env(), windowId), cursor, config });
+ } else {
+ call("setCustomCursor", {Napi::Number::New(env(), windowId), Napi::Value::From(env(), pixelMap),
+ Napi::Number::New(env(), info->focusX), Napi::Number::New(env(), info->focusY)});
+ }
+ ::OH_PixelmapNative_Release(nativePixel);
+ });
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,79 @@
+#ifndef QJSCURSOR_H
+#define QJSCURSOR_H
+
+#include <QPixmap>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+struct QCursorInfo {
+ int shape;
+ int focusX = 0;
+ int focusY = 0;
+ QPixmap pixMap;
+};
+
+class QJsCursor : public QJsModule
+{
+public:
+ enum PointerStyle{
+ DEFAULT,
+ EAST,
+ WEST,
+ SOUTH,
+ NORTH,
+ WEST_EAST,
+ NORTH_SOUTH,
+ NORTH_EAST,
+ NORTH_WEST,
+ SOUTH_EAST,
+ SOUTH_WEST,
+ NORTH_EAST_SOUTH_WEST,
+ NORTH_WEST_SOUTH_EAST,
+ CROSS,
+ CURSOR_COPY,
+ CURSOR_FORBID,
+ COLOR_SUCKER,
+ HAND_GRABBING,
+ HAND_OPEN,
+ HAND_POINTING,
+ HELP,
+ MOVE,
+ RESIZE_LEFT_RIGHT,
+ RESIZE_UP_DOWN,
+ SCREENSHOT_CHOOSE,
+ SCREENSHOT_CURSOR,
+ TEXT_CURSOR,
+ ZOOM_IN,
+ ZOOM_OUT,
+ MIDDLE_BTN_EAST,
+ MIDDLE_BTN_WEST,
+ MIDDLE_BTN_SOUTH,
+ MIDDLE_BTN_NORTH,
+ MIDDLE_BTN_NORTH_SOUTH,
+ MIDDLE_BTN_NORTH_EAST,
+ MIDDLE_BTN_NORTH_WEST,
+ MIDDLE_BTN_SOUTH_EAST,
+ MIDDLE_BTN_SOUTH_WEST,
+ MIDDLE_BTN_NORTH_SOUTH_WEST_EAST,
+ HORIZONTAL_TEXT_CURSOR,
+ CURSOR_CROSS,
+ CURSOR_CIRCLE,
+ LOADING,
+ RUNNING,
+ HIDE,
+ CUSTOM=99
+ };
+ QJsCursor();
+ ~QJsCursor();
+
+ bool isVisible() const;
+
+ void setVisible(bool visible);
+
+ void setCursor(int windowId, QCursorInfo *info);
+
+};
+
+QT_END_NAMESPACE
+#endif // QJSCURSOR_H
new file mode 100644
@@ -0,0 +1,38 @@
+#include "qjsdeviceinfo.h"
+#include <qopenharmony.h>
+#include <QtCore/QRegularExpression>
+
+Q_GLOBAL_STATIC(QJsDeviceInfo, info)
+QT_BEGIN_NAMESPACE
+
+QJsDeviceInfo::QJsDeviceInfo()
+ : QJsModule("@ohos.deviceInfo")
+{
+}
+
+QJsDeviceInfo *QJsDeviceInfo::instance()
+{
+ return info();
+}
+
+QString QJsDeviceInfo::deviceType() const
+{
+ Napi::Value result = get("deviceType");
+ return QString::fromStdString(result.ToString());
+}
+
+QString QJsDeviceInfo::displayVersion() const
+{
+ // HYM-W5821 2.0.0.66(SP59DEVC00E66R1P4log)
+ Napi::Value result = get("displayVersion");
+ QString dv = QString::fromStdString(result.ToString());
+ QRegularExpression regex("(\\d+\\.\\d+\\.\\d+\\.\\d+)");
+ QRegularExpressionMatch match = regex.match(dv);
+ if (match.hasMatch()) {
+ QString version = match.captured(1);
+ return version;
+ }
+ return QString();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QJSDEVICEINFO_H
+#define QJSDEVICEINFO_H
+
+#include <QSize>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+class QJsDeviceInfo : public QJsModule
+{
+public:
+ QJsDeviceInfo();
+
+ static QJsDeviceInfo *instance();
+
+ QString deviceType() const;
+
+ QString displayVersion() const;
+};
+
+QT_END_NAMESPACE
+#endif // QJSDEVICEINFO_H
new file mode 100644
@@ -0,0 +1,115 @@
+#include <qopenharmonydefines.h>
+#include <window_manager/oh_display_info.h>
+#include <window_manager/oh_display_manager.h>
+
+#include "qohmain.h"
+#include "qjsscreen.h"
+#include "qjsdisplay.h"
+#include "qohauxiliary.h"
+#include "qohjsonlistener.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsDisplay::QJsDisplay()
+ : QJsModule("@ohos.display")
+{
+ m_self = this;
+}
+
+QJsDisplay::~QJsDisplay()
+{
+ OH_NativeDisplayManager_UnregisterDisplayChangeListener(m_listenerIndex);
+ for (int i = 0; i < m_listeners.count(); ++i) {
+ m_listeners.at(i)->off();
+ }
+ qDeleteAll(m_listeners);
+ m_self = nullptr;
+}
+
+
+void QJsDisplay::initialize()
+{
+ // defaultDisplayInfo = getDisplayInfoForPrimaryDisplay(jsState);
+ auto screen = getPrimaryDisplay();
+ if (Q_UNLIKELY(screen == nullptr)) {
+ //fatal error
+ LOGE("cannot get primary display");
+ return;
+ }
+ m_screens.append(screen);
+
+ this->initialize(CreateInfo{
+ .displayChangeCb = &QJsDisplay::displayChangeCallback,
+ .displayAddedCb = [](uint64_t displayId) {
+ //todo
+ },
+ .displayRemovedCb = [](uint64_t displayId) {
+ //todo
+ },
+ });
+}
+
+void QJsDisplay::initialize(const CreateInfo& createInfo)
+{
+ NativeDisplayManager_ErrorCode errCode = NativeDisplayManager_ErrorCode::DISPLAY_MANAGER_OK;
+ errCode = OH_NativeDisplayManager_RegisterDisplayChangeListener(createInfo.displayChangeCb, &m_listenerIndex);
+ if (DISPLAY_MANAGER_OK != errCode) {
+ LOGE("register display change listener failed, err code:[%{public}d]", errCode);
+ }
+
+ QtOh::runOnJsUIThreadAndWait([this, createInfo]{
+ m_listeners << new QOhJsOnListener(this, "add", [this, createInfo](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1 || !info[0].IsNumber()) {
+ return;
+ }
+ Napi::Number displayId = info[0].As<Napi::Number>();
+
+ createInfo.displayAddedCb(displayId.Uint32Value());
+ });
+ m_listeners << new QOhJsOnListener(this, "remove", [createInfo](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1 || !info[0].IsNumber()) {
+ return;
+ }
+ Napi::Number displayId = info[0].As<Napi::Number>();
+
+ createInfo.displayRemovedCb(displayId.Uint32Value());
+ });
+ for (int i = 0; i < m_listeners.count(); ++i) {
+ m_listeners.at(i)->on();
+ }
+ });
+}
+
+QJsScreen *QJsDisplay::defaultScreen() const
+{
+ return m_screens.at(0);
+}
+
+QJsScreen *QJsDisplay::getPrimaryDisplay()
+{
+ Napi::Value result = call("getPrimaryDisplaySync");
+ QJsScreen *screen = result.IsObject() ? new QJsScreen(result.As<Napi::Object>()) : nullptr;
+ return screen;
+}
+
+void QJsDisplay::displayChangeCallback(uint64_t displayId)
+{
+ auto it = std::find_if(m_screens.constBegin(), m_screens.constEnd(), [displayId](QJsScreen *s){
+ return s->id() == displayId;
+ });
+ if (it == m_screens.constEnd())
+ return;
+ QJsScreen *s = *it;
+ QtOh::runOnQtMainThread([s, displayId]{
+ if (m_self == nullptr)
+ return;
+ Napi::Value result = m_self->call("getDisplayByIdSync", {Napi::Number::New(m_self->env(), displayId)});
+ if (result.IsObject()) {
+ s->setObject(result.As<Napi::Object>());
+ s->reObtainInfo();
+ }
+ });
+ LOGW("displayChangeCallback displayId=%{public}lu.", displayId);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,39 @@
+#ifndef QJSDISPLAY_H
+#define QJSDISPLAY_H
+
+#include <QSize>
+#include <QJsModule>
+#include <QList>
+class QOhJsOnListener;
+QT_BEGIN_NAMESPACE
+
+class QJsScreen;
+class QJsDisplay : public QJsModule
+{
+public:
+ QJsDisplay();
+ ~QJsDisplay();
+
+ QJsScreen *defaultScreen() const;
+ typedef void (*DisplayChangeCallback)(uint64_t displayId);
+
+ struct CreateInfo
+ {
+ DisplayChangeCallback displayChangeCb;
+ std::function<void(uint64_t)> displayAddedCb;
+ std::function<void(uint64_t)> displayRemovedCb;
+ };
+ void initialize();
+ void initialize(const CreateInfo& info);
+ static void displayChangeCallback(uint64_t displayId);
+private:
+ QJsScreen *getPrimaryDisplay();
+private:
+ static inline QList<QJsScreen *> m_screens = {};
+ static inline uint32_t m_listenerIndex = 0;
+ static inline QJsDisplay *m_self = nullptr;
+ QList<QOhJsOnListener *> m_listeners;
+};
+
+QT_END_NAMESPACE
+#endif // QJSDISPLAY_H
new file mode 100644
@@ -0,0 +1,60 @@
+#include "qjsinputmethod.h"
+#include "qjsinputmethodcontroller.h"
+#include <QPointer>
+#include <QRect>
+#include <QDebug>
+namespace QJsInputMethod
+{
+QJsInputMethodController *getController()
+{
+ return QtOh::runOnJsUIThreadWithResult([]{
+ return new QJsInputMethodController(QSharedPointer<QJsModule>::create("@ohos.inputMethod").data());
+ });
+}
+
+const QStringList InputAttributeKeys = {"textInputType" , "enterKeyType"};
+QJsInputAttribute::QJsInputAttribute(QJsTextInputType inputType, QJsEnterKeyType enterType)
+ :m_values({int(inputType), int(enterType)})
+{
+}
+
+Napi::Object QJsInputAttribute::toObject(napi_env env) const
+{
+ Napi::Object obj = Napi::Object::New(env);
+ for(int i = 0; i < InputAttributeKeys.size(); i++)
+ {
+ obj.Set(InputAttributeKeys[i].toStdString(), Napi::Number::New(env, m_values[i]));
+ }
+ return obj;
+}
+
+QJsTextConfig::QJsTextConfig(const QJsInputAttribute &attribute)
+ :m_attribute(attribute)
+{
+}
+
+Napi::Object QJsTextConfig::toObject(napi_env env) const
+{
+ Napi::Object obj = Napi::Object::New(env);
+ obj.Set("inputAttribute", m_attribute.toObject(env));
+ return obj;
+}
+
+const QStringList cursorInfoKeys = {"left", "top", "width", "height"};
+QJsCursorInfo::QJsCursorInfo(int left, int top, int width, int height)
+ :m_values({left, top, width, height})
+{
+}
+
+Napi::Object QJsCursorInfo::toObject(napi_env env) const
+{
+ Napi::Object obj = Napi::Object::New(env);
+ for(int i = 0; i < cursorInfoKeys.size(); i++)
+ {
+ obj.Set(cursorInfoKeys[i].toStdString(), Napi::Number::New(env, m_values[i]));
+ }
+ return obj;
+}
+
+}
+
new file mode 100644
@@ -0,0 +1,169 @@
+#ifndef QJSINPUTMETHOD_H
+#define QJSINPUTMETHOD_H
+#include <qopenharmony.h>
+#include <QRect>
+#include <QJsObject>
+namespace QJsInputMethod
+{
+ class QJsInputMethodController;
+ enum QJsTextInputType {
+ /**
+ * The text input type is NONE.
+ * @since 10
+ */
+ NONE_TEXT = -1,
+ /**
+ * The text input type is TEXT.
+ * @since 10
+ */
+ TEXT = 0,
+ /**
+ * The text input type is MULTILINE.
+ * @since 10
+ */
+ MULTILINE,
+ /**
+ * The text input type is NUMBER.
+ * @since 10
+ */
+ NUMBER,
+ /**
+ * The text input type is PHONE.
+ * @since 10
+ */
+ PHONE,
+ /**
+ * The text input type is DATETIME.
+ * @since 10
+ */
+ DATETIME,
+ /**
+ * The text input type is EMAIL_ADDRESS.
+ * @since 10
+ */
+ EMAIL_ADDRESS,
+ /**
+ * The text input type is URL.
+ * @since 10
+ */
+ URL,
+ /**
+ * The text input type is VISIBLE_PASSWORD.
+ * @since 10
+ */
+ VISIBLE_PASSWORD,
+ /**
+ * The text input type is NUMBER_PASSWORD.
+ * @since 11
+ */
+ NUMBER_PASSWORD
+ };
+ /**
+ * Enumerates the enter key type.
+ *
+ * @enum { number }
+ * @syscap SystemCapability.MiscServices.InputMethodFramework
+ * @since 10
+ */
+ enum QJsEnterKeyType {
+ /**
+ * The enter key type is UNSPECIFIED.
+ * @since 10
+ */
+ UNSPECIFIED = 0,
+ /**
+ * The enter key type is NONE.
+ * @since 10
+ */
+ NONE_ENTER = 1,
+ /**
+ * The enter key type is GO.
+ * @since 10
+ */
+ GO,
+ /**
+ * The enter key type is SEARCH.
+ * @since 10
+ */
+ SEARCH,
+ /**
+ * The enter key type is SEND.
+ * @since 10
+ */
+ SEND,
+ /**
+ * The enter key type is NEXT.
+ * @since 10
+ */
+ NEXT,
+ /**
+ * The enter key type is DONE.
+ * @since 10
+ */
+ DONE,
+ /**
+ * The enter key type is PREVIOUS.
+ * @since 10
+ */
+ PREVIOUS,
+ /**
+ * The enter key type is NEWLINE.
+ * @since 12
+ */
+ NEWLINE
+ };
+
+ enum QJsRequestKeyboardReason {
+ /**
+ * The request keyboard reason is NONE.
+ * @since 15
+ */
+ NONE = 0,
+ /**
+ * The request keyboard reason is MOUSE.
+ * @since 15
+ */
+ MOUSE = 1,
+ /**
+ * The request keyboard reason is TOUCH.
+ * @since 15
+ */
+ TOUCH = 2,
+ /**
+ * The request keyboard reason is OTHER.
+ * @since 15
+ */
+ OTHER = 20
+ };
+ class QJsInputAttribute
+ {
+ public:
+ QJsInputAttribute(QJsTextInputType, QJsEnterKeyType);
+ Napi::Object toObject(napi_env) const;
+ private:
+ QList<int> m_values = {0, 0};
+ };
+ class QJsCursorInfo
+ {
+ public:
+ QJsCursorInfo(int left, int top, int width, int height);
+ Napi::Object toObject(napi_env) const;
+ private:
+ QList<int> m_values = {0, 0, 0, 0};
+ };
+ class QJsTextConfig
+ {
+ public:
+ QJsTextConfig(const QJsInputAttribute &);
+ Napi::Object toObject(napi_env) const;
+ private:
+ QJsInputAttribute m_attribute;
+ };
+ /**
+ * @brief getController
+ * @return
+ */
+ QJsInputMethodController *getController();
+} // namespace QJsInputMethod
+
+#endif // QJSINPUTMETHOD_H
new file mode 100644
@@ -0,0 +1,361 @@
+#include "qjsinputmethodcontroller.h"
+#include "qjsinputmethod.h"
+#include "qohplatforminputcontext.h"
+#include "qohwindowcontext.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include <QRect>
+#include <QDebug>
+namespace QJsInputMethod
+{
+class QJsInputMethodControllerPrivate
+{
+public:
+ QJsInputMethodControllerPrivate(QJsInputMethodController *controller)
+ : q_ptr(controller)
+ {
+ Q_UNUSED(q_ptr);
+ }
+ void subscribe()
+ {
+ QHash<QString, Napi::Function>::const_iterator i = m_functions.constBegin();
+ while (i != m_functions.constEnd()) {
+ q_ptr->call("on", {Napi::String::New(q_ptr->env(), i.key().toStdString()), i.value()});
+ ++i;
+ }
+ }
+ void unsubscribe()
+ {
+ QHash<QString, Napi::Function>::const_iterator i = m_functions.constBegin();
+ while (i != m_functions.constEnd()) {
+ q_ptr->call("off", {Napi::String::New(q_ptr->env(), i.key().toStdString())});
+ ++i;
+ }
+ }
+ bool m_attached = false;
+ bool m_visible = false;
+ QHash<QString, Napi::Function> m_functions;
+private:
+ QJsInputMethodController *q_ptr;
+};
+void QJsInputMethodController::on(QJsSubEvent *event) const
+{
+ if (event)
+ {
+ d_ptr->m_functions.insert(QString::fromStdString(event->type), event->callback);
+ }
+}
+
+void QJsInputMethodController::off(QJsSubEvent *event) const
+{
+ if (event)
+ {
+ d_ptr->m_functions.remove(QString::fromStdString(event->type));
+ delete event;
+ event = nullptr;
+ }
+}
+
+bool QJsInputMethodController::isAttached() const
+{
+ return d_ptr->m_attached;
+}
+
+#if OHOS_SDK_VERSION >= 15
+bool QJsInputMethodController::attach(const bool &, const QJsTextConfig &textConfig, QJsRequestKeyboardReason reason)
+{
+ qDebug() << "attach reason:" << reason;
+ if (isAttached()) {
+ return true;
+ }
+ d_ptr->m_attached = QtOh::runOnJsUIThreadWithPromise<bool>([&textConfig, reason, this](auto p){
+ Napi::HandleScope scope(env());
+ QJsPromise promise(call("attach", {Napi::Boolean::New(env(), true),
+ textConfig.toObject(env()),
+ Napi::Number::New(env(), reason)
+ }).As<Napi::Promise>());
+ promise.onThen([this, p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ d_ptr->subscribe();
+ p->set_value(true);
+ }).onCatch([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ p->set_value(false);
+ });
+ });
+ d_ptr->m_visible = d_ptr->m_attached;
+ return isAttached();
+}
+void QJsInputMethodController::showTextInput(QJsRequestKeyboardReason reason)
+{
+ qDebug() << "showTextInput reason:" << reason;
+ QtOh::runOnJsUIThreadAndWait([reason, this]{
+ QJsPromise promise(call("showTextInput", {Napi::Number::New(env(), reason)}).As<Napi::Promise>());
+ promise.onThen([](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ });
+ });
+}
+#else
+bool QJsInputMethodController::attach(const bool &, const QJsTextConfig &textConfig)
+{
+ if (isAttached()) {
+ return true;
+ }
+ d_ptr->m_attached = QtOh::runOnJsUIThreadWithPromise<bool>([&textConfig, this](auto p){
+ Napi::HandleScope scope(env());
+ QJsPromise promise(call("attach", { Napi::Boolean::New(env(), true), textConfig.toObject(env()) }).As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ d_ptr->subscribe();
+ p->set_value(true);
+ }.onCatch([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ p->set_value(false);
+ });
+ });
+ d_ptr->m_visible = d_ptr->m_attached;
+ return isAttached();
+}
+
+void QJsInputMethodController::showTextInput()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ QJsPromise promise(call("showTextInput").As<Napi::Promise>());
+ promise.onThen([](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ });
+ });
+}
+#endif
+
+void QJsInputMethodController::hideTextInput()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ QJsPromise promise(call("hideTextInput").As<Napi::Promise>());
+ promise.onThen([](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ });
+ });
+}
+
+bool QJsInputMethodController::detach()
+{
+ if (!isAttached())
+ return true;
+
+ d_ptr->m_attached = QtOh::runOnJsUIThreadWithPromise<bool>([this](auto p){
+ d_ptr->unsubscribe();
+ QJsPromise promise(call("detach").As<Napi::Promise>());
+ promise.onThen([this, p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ p->set_value(false);
+ }).onCatch([this](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ p->set_value(true);
+ });
+ });
+ d_ptr->m_visible = d_ptr->m_attached;
+ return !isAttached();
+}
+
+void QJsInputMethodController::setCallingWindow(int windowId)
+{
+ QtOh::runOnJsUIThreadAndWait([windowId, this]{
+ call("setCallingWindow", { Napi::Number::New(env(), windowId) });
+ });
+}
+
+void QJsInputMethodController::updateCursor(const QJsCursorInfo &cursorInfo)
+{
+ QtOh::runOnJsUIThreadAndWait([&cursorInfo, this]{
+ call("updateCursor", {cursorInfo.toObject(env())});
+ });
+}
+
+void QJsInputMethodController::changeSelection(const QString &text, int start, int end)
+{
+ QtOh::runOnJsUIThreadAndWait([text, start, end, this]{
+ call("changeSelection", {Napi::String::New(env(), text.toStdString()),
+ Napi::Number::New(env(), start),
+ Napi::Number::New(env(), end)});
+ });
+}
+
+void QJsInputMethodController::updateAttribute(const QJsInputAttribute &attribute)
+{
+ QtOh::runOnJsUIThreadAndWait([&attribute, this]{
+ call("updateAttribute", {attribute.toObject(env())});
+ });
+}
+
+void QJsInputMethodController::stopInputSession()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ call("stopInputSession");
+ });
+}
+
+void QJsInputMethodController::showSoftKeyboard()
+{
+ QtOh::runOnJsUIThreadAndWait([=, this]{
+ auto asyncFunc = Napi::Function::New(env(), [this](const Napi::CallbackInfo& info){
+ Q_UNUSED(info);
+ d_ptr->m_visible = true;
+ });
+ call("showSoftKeyboard", {asyncFunc});
+ });
+}
+
+void QJsInputMethodController::hideSoftKeyboard()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ auto asyncFunc = Napi::Function::New(env(), [this](const Napi::CallbackInfo& info){
+ Q_UNUSED(info);
+ d_ptr->m_visible = false;
+ });
+ call("hideSoftKeyboard", { asyncFunc });
+ });
+}
+
+bool QJsInputMethodController::isInputPanelVisible() const
+{
+ return d_ptr->m_visible;
+}
+
+void QJsInputMethodController::setInputPanelVisible(bool visible)
+{
+ d_ptr->m_visible = visible;
+}
+
+static QJsSubEvent *insertTextEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::insertText()
+{
+ if (!insertTextEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ auto context = QOhPlatformInputContext::ohInputContext();
+ insertTextEvent = new QJsSubEvent("insertText", [context](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1)
+ return;
+ QString text = QString::fromStdString(info[0].ToString());
+ QMetaObject::invokeMethod(context,
+ "commitText", Qt::QueuedConnection, Q_ARG(QString, text));
+ }, context->inputMethodController()->env());
+ });
+ return insertTextEvent;
+}
+
+static QJsSubEvent *sendFunctionKeyEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::sendFunctionKey()
+{
+ if (!sendFunctionKeyEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ auto context = QOhPlatformInputContext::ohInputContext();
+ sendFunctionKeyEvent = new QJsSubEvent("sendFunctionKey", [context](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info);
+ QMetaObject::invokeMethod(context, "enterKeyDown", Qt::QueuedConnection);
+ }, context->inputMethodController()->env());
+ });
+ return sendFunctionKeyEvent;
+}
+
+static QJsSubEvent *sendKeyboardStatusEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::sendKeyboardStatus()
+{
+ if (!sendKeyboardStatusEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ sendKeyboardStatusEvent = new QJsSubEvent("sendKeyboardStatus", [](const Napi::CallbackInfo& info) {
+ if (!QOhPlatformInputContext::inputMethodController())
+ return;
+ if (info.Length() < 1)
+ return;
+ int status = info[0].ToNumber();
+ switch (status) {
+ //NOTE:只能收到NONE(12 release beta1)
+ case 0:
+ case 1:
+ QOhPlatformInputContext::inputMethodController()->setInputPanelVisible(false);
+ QOhPlatformInputContext::inputMethodController()->detach();
+ break;
+ case 2:
+ QOhPlatformInputContext::inputMethodController()->setInputPanelVisible(true);
+ break;
+ default:
+ break;
+ }
+ }, QOhPlatformInputContext::inputMethodController()->env());
+ });
+ return sendKeyboardStatusEvent;
+}
+
+static QJsSubEvent *deleteLeftEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::deleteLeft()
+{
+ if (!deleteLeftEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ auto context = QOhPlatformInputContext::ohInputContext();
+ deleteLeftEvent = new QJsSubEvent("deleteLeft", [context](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info);
+ Qt::KeyboardModifiers mods = context->queryKeyboardModifiers();
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyPress, Qt::Key_Backspace,
+ mods, KEY_DEL, KEY_DEL, int(mods));
+
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyRelease, Qt::Key_Backspace,
+ mods, KEY_DEL, KEY_DEL, int(mods));
+ }, context->inputMethodController()->env());
+ });
+ return deleteLeftEvent;
+
+}
+static QJsSubEvent *deleteRightEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::deleteRight()
+{
+ if (!deleteRightEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ auto context = QOhPlatformInputContext::ohInputContext();
+ deleteRightEvent = new QJsSubEvent("deleteRight", [context](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info);
+ Qt::KeyboardModifiers mods = context->queryKeyboardModifiers();
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyPress, Qt::Key_Delete,
+ mods, KEY_FORWARD_DEL, KEY_FORWARD_DEL, int(mods));
+
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyRelease, Qt::Key_Delete,
+ mods, KEY_FORWARD_DEL, KEY_FORWARD_DEL, int(mods));
+ }, context->inputMethodController()->env());
+ });
+ return deleteRightEvent;
+}
+
+static QJsSubEvent *moveCursorEvent = nullptr;
+QJsSubEvent *QJsInputMethodController::moveCursor()
+{
+ if (!moveCursorEvent || !QOhPlatformInputContext::inputMethodController())
+ QtOh::runOnJsUIThreadAndWait([]{
+ auto context = QOhPlatformInputContext::ohInputContext();
+ moveCursorEvent = new QJsSubEvent("moveCursor", [context](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1)
+ return;
+ int direction = info[0].ToNumber();
+ QMetaObject::invokeMethod(context, "commitMoveCursor", Qt::QueuedConnection, Q_ARG(int, direction));
+ }, context->inputMethodController()->env());
+ });
+ return moveCursorEvent;
+}
+
+QJsInputMethodController::QJsInputMethodController(QJsModule *inputMethod)
+ : QJsObject(inputMethod->call("getController").As<Napi::Object>()),
+ d_ptr(new QJsInputMethodControllerPrivate(this))
+{
+
+}
+
+QJsInputMethodController::~QJsInputMethodController()
+{
+ detach();
+ delete d_ptr;
+}
+}
new file mode 100644
@@ -0,0 +1,59 @@
+#ifndef QJSINPUTMETHODCONTROLLER_H
+#define QJSINPUTMETHODCONTROLLER_H
+// #include "qjsinputmethod.h"
+
+#include "qjsinputmethod.h"
+#include <QJsModule>
+namespace QJsInputMethod
+{
+
+class QJsInputMethodControllerPrivate;
+
+
+class QJsInputMethodController : public QJsObject
+{
+public:
+
+ ~QJsInputMethodController();
+ bool isAttached() const;
+#if OHOS_SDK_VERSION >= 15
+
+ bool attach(const bool &, const QJsTextConfig &, QJsRequestKeyboardReason reason = QJsRequestKeyboardReason::OTHER);
+ void showTextInput(QJsRequestKeyboardReason reason = QJsRequestKeyboardReason::OTHER);
+#else
+ bool attach(const bool &, const QJsTextConfig &);
+ void showTextInput();
+#endif
+ void hideTextInput();
+ bool detach();
+ void setCallingWindow(int windowId);
+ void updateCursor(const QJsCursorInfo &);
+ void changeSelection(const QString &, int, int);
+ void updateAttribute(const QJsInputAttribute &);
+ void stopInputSession();
+ void showSoftKeyboard();
+ void hideSoftKeyboard();
+ bool isInputPanelVisible() const;
+ void setInputPanelVisible(bool visible);
+ /**
+ * @brief InputMethodController订阅事件
+ */
+public:
+ void on(QJsSubEvent *) const;
+ void off(QJsSubEvent *) const;
+ static QJsSubEvent *insertText();
+ static QJsSubEvent *sendFunctionKey();
+ static QJsSubEvent *sendKeyboardStatus();
+ static QJsSubEvent *deleteLeft();
+ static QJsSubEvent *deleteRight();
+ static QJsSubEvent *moveCursor();
+
+private:
+ friend QJsInputMethodController* QJsInputMethod::getController();
+ QJsInputMethodController(QJsModule *inputMethod);
+
+ QJsInputMethodControllerPrivate *d_ptr = nullptr;
+};
+}; // namespace QJsInputMethod
+
+#endif // QJSINPUTMETHODCONTROLLER_H
new file mode 100644
@@ -0,0 +1,199 @@
+#include <QDebug>
+
+#include "qohmain.h"
+#include "qjsscreen.h"
+#include <qopenharmony.h>
+#include "qohauxiliary.h"
+#include "qohjsonlistener.h"
+#include "qohplatformscreen.h"
+#include <private/qjspromise_p.h>
+QT_BEGIN_NAMESPACE
+
+QJsScreen::QJsScreen(Napi::Object jsObject)
+ : QJsObject(jsObject)
+ , m_platformScreen(nullptr)
+{
+ obtainInfo();
+ m_availableAreaChangeListener = new QOhJsOnListener(this, "availableAreaChange", [this](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1 || !info[0].IsObject()) {
+ return;
+ }
+ auto rectObj = info[0].ToObject();
+ QRect rect = QtOh::jsRect2QRect(rectObj);
+
+ qDebug() << "availableAreaChange" << this->m_info.id << rect;
+
+ QtOh::runOnQtMainThread([this, rect]{
+ if (m_platformScreen) {
+ m_platformScreen->setAvailableGeometry(rect);
+ }
+ });
+ });
+ m_availableAreaChangeListener->on();
+}
+
+QJsScreen::~QJsScreen()
+{
+ m_availableAreaChangeListener->off();
+ delete m_availableAreaChangeListener;
+}
+
+void QJsScreen::attach(QOhPlatformScreen *platformScreen)
+{
+ m_platformScreen = platformScreen;
+}
+
+uint32_t QJsScreen::id() const
+{
+ return m_info.id;
+}
+
+Qt::ScreenOrientation QJsScreen::orientation() const
+{
+ return m_info.orientation;
+}
+
+QSize QJsScreen::size() const
+{
+ return m_info.s;
+}
+
+QRect QJsScreen::availableGeometry() const
+{
+ return m_info.available;
+}
+
+QSize QJsScreen::physicalSize() const
+{
+ return m_info.ps;
+}
+
+qreal QJsScreen::densityPixels() const
+{
+ return m_info.densityPixels;
+}
+
+qreal QJsScreen::scaledDensity() const
+{
+ return m_info.scaledDensity;
+}
+
+qreal QJsScreen::oldScaledDensity() const
+{
+ return m_info.oldScaledDensity;
+}
+
+qreal QJsScreen::pixelDensity() const
+{
+ return m_info.densityDPI / 160; // 96?
+}
+
+int QJsScreen::refreshRate() const
+{
+ return m_info.refreshRate;
+}
+
+qreal QJsScreen::densityDPI() const
+{
+ return m_info.densityDPI;
+}
+
+qreal QJsScreen::xDPI() const
+{
+ return m_info.xDPI;
+}
+
+qreal QJsScreen::yDPI() const
+{
+ return m_info.yDPI;
+}
+
+void QJsScreen::reObtainInfo()
+{
+ ScreenInfo old = m_info;
+ obtainInfo();
+
+ bool orientationChanged = (old.orientation != m_info.orientation);
+ if (orientationChanged)
+ m_platformScreen->handleScreenOrientationChange();
+ bool refreshRateChanged = (old.refreshRate != m_info.refreshRate);
+ if (refreshRateChanged)
+ m_platformScreen->handlerefreshRateChange(m_info.refreshRate);
+
+ bool scaledDensityChanged = !qFuzzyCompare(old.scaledDensity, m_info.scaledDensity);
+ if (scaledDensityChanged)
+ m_platformScreen->handleScreenLogicalDotsPerInchChange();
+ if (m_info.ps != old.ps)
+ m_platformScreen->setPhysicalSize(m_info.ps);
+ if (m_info.available != old.available)
+ m_platformScreen->setAvailableGeometry(availableGeometry());
+ m_platformScreen->setSize(m_info.s);
+ if (scaledDensityChanged) {
+ m_info.oldScaledDensity = old.scaledDensity;
+ m_platformScreen->handleScreenChanged();
+ }
+}
+
+void QJsScreen::obtainInfo()
+{
+ Napi::Value width = get("width");
+ Napi::Value height = get("height");
+ QSize s = QSize(width.ToNumber().Int32Value(), height.ToNumber().Int32Value());
+ m_info.s = s;
+ m_info.id = get("id").ToNumber().Int32Value();
+ Napi::Value densityPixels = get("densityPixels");
+ Napi::Value scaledDensity = get("scaledDensity");
+ m_info.densityPixels = densityPixels.ToNumber().DoubleValue();
+ m_info.scaledDensity = scaledDensity.ToNumber().DoubleValue();
+ m_info.oldScaledDensity = m_info.scaledDensity;
+ m_info.refreshRate = get("refreshRate").ToNumber().Int32Value();
+
+ // openharmony old api, xdpi and ydpi is 0
+ double xdpi = get("xDPI").ToNumber().DoubleValue();
+ double ydpi = get("yDPI").ToNumber().DoubleValue();
+
+ double densityDPI = get("densityDPI").ToNumber().DoubleValue();
+ if (qFuzzyCompare(xdpi, 0))
+ xdpi = densityDPI;
+ if (qFuzzyCompare(ydpi, 0))
+ ydpi = densityDPI;
+
+ m_info.xDPI = xdpi;
+ m_info.yDPI = ydpi;
+ m_info.densityDPI = densityDPI;
+ m_info.ps = QSize(qRound(double(s.width()) / xdpi * 25.4), qRound(double(s.height()) / ydpi * 25.4));
+ int32_t orientation = get("orientation").ToNumber();
+ switch (orientation) {
+ case 0:
+ m_info.orientation = Qt::PortraitOrientation;
+ break;
+ case 1:
+ m_info.orientation = Qt::LandscapeOrientation;
+ break;
+ case 2:
+ m_info.orientation = Qt::InvertedPortraitOrientation;
+ break;
+ case 3:
+ m_info.orientation = Qt::InvertedLandscapeOrientation;
+ break;
+ }
+
+ const QtOh::Utility::DeviceType deviceType = QtOh::Utility::type();
+ if (deviceType == QtOh::Utility::PC || deviceType == QtOh::Utility::Tablet) {
+ Napi::Value result = call("getAvailableArea");
+ if (!result.IsPromise()) {
+ return;
+ }
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([&](const Napi::CallbackInfo &info) {
+ if (info.Length() < 1)
+ return;
+ Napi::Object object = info[0].ToObject();
+ m_info.available = QtOh::jsRect2QRect(object);
+ });
+ } else {
+ m_info.available = QRect(0, 0, s.width(), s.height());
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,73 @@
+#ifndef QJSSCREEN_H
+#define QJSSCREEN_H
+
+#include "qjsobject.h"
+
+#include <QSize>
+#include <QRect>
+
+QT_BEGIN_NAMESPACE
+class QOhPlatformScreen;
+class QOhJsOnListener;
+class QJsScreen : public QJsObject
+{
+ struct ScreenInfo
+ {
+ uint32_t id;
+ QSize s;
+ QSize ps;
+ QRect available;
+ int densityDPI;
+ qreal xDPI;
+ qreal yDPI;
+ qreal densityPixels;
+ qreal scaledDensity;
+ qreal oldScaledDensity;
+ int refreshRate;
+ Qt::ScreenOrientation orientation;
+ };
+
+public:
+ QJsScreen(Napi::Object jsObject);
+ ~QJsScreen();
+
+ void attach(QOhPlatformScreen *platformScreen);
+
+ uint32_t id() const;
+
+ Qt::ScreenOrientation orientation() const;
+
+ QSize size() const;
+
+ QRect availableGeometry() const;
+
+ QSize physicalSize() const;
+
+ qreal densityPixels() const;
+
+ qreal scaledDensity() const;
+
+ qreal oldScaledDensity() const;
+
+ qreal pixelDensity() const;
+
+ int refreshRate() const;
+
+ qreal densityDPI() const;
+
+ qreal xDPI() const;
+
+ qreal yDPI() const;
+
+ void reObtainInfo();
+
+private:
+ void obtainInfo();
+private:
+ ScreenInfo m_info;
+ QOhPlatformScreen *m_platformScreen;
+ QOhJsOnListener *m_availableAreaChangeListener;
+};
+
+QT_END_NAMESPACE
+#endif // QJSSCREEN_H
new file mode 100644
@@ -0,0 +1,116 @@
+#include <qdebug.h>
+
+#include "qjssettings.h"
+#include "qjscontext.h"
+#include "qohauxiliary.h"
+#include "qohnativewindow.h"
+#include "qohnativewindowmanager.h"
+#include <private/qopenharmony_p.h>
+
+namespace DomainName {
+const char DEVICE_SHARED[] = "global";
+const char USER_PROPERTY[] = "system";
+}
+
+QT_BEGIN_NAMESPACE
+Q_GLOBAL_STATIC(QJsSettings, settings)
+
+QJsSettings::QJsSettings()
+ : QJsModule("@ohos.settings")
+{
+ m_freeWindowEnable = getValueSync("window_pcmode_switch_status", "false", DomainName::USER_PROPERTY)== "true";
+
+ registerKeyObserver("window_pcmode_switch_status", [this]{
+ if (!QtOh::isPhoneDevice() && !QtOh::isTabletDevice()) {
+ return;
+ }
+
+ bool isFreeWindowEnable = QJsSettings::instance()->isFreeWindowEnable();
+ QtOh::runOnQtMainThread([isFreeWindowEnable]{
+ for (QOhNativeWindow *nativeWindow : QOhNativeWindow::allWindows()) {
+ nativeWindow->handleFreeWindowEnableChanged(isFreeWindowEnable);
+ }
+ });
+
+ auto extraHandler = get("pcModeChangedListener");
+ if (extraHandler.IsEmpty() || !extraHandler.IsFunction())
+ return;
+ call("pcModeChangedListener", {Napi::Boolean::New(env(), isFreeWindowEnable)});
+
+ }, true, qNativeWindowManager->context());
+}
+
+QJsSettings::~QJsSettings()
+{
+ unregisterKeyObserver("window_pcmode_switch_status", true, qNativeWindowManager->context());
+}
+
+
+
+bool QJsSettings::isFreeWindowEnable() const
+{
+ m_freeWindowEnable = getValueSync("window_pcmode_switch_status", "false", DomainName::USER_PROPERTY)== "true";
+ return m_freeWindowEnable;
+}
+
+QString QJsSettings::getValueFromUserDomain(const QString &name) const
+{
+ return getValueSync(name, "", DomainName::USER_PROPERTY);
+}
+
+QString QJsSettings::getValueFromSharedDomain(const QString &name) const
+{
+ return getValueSync(name, "", DomainName::DEVICE_SHARED);
+}
+
+QString QJsSettings::getValueSync(const QString &name, const QString &defaultValue, const QString &domainName) const
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return QString();
+ return QtOh::runOnJsUIThreadWithResult([this, context](const QString &defaultValue, const QString &name, const QString &domainName){
+ return call<QString>("getValueSync", { context->object(), Napi::String::New(env(), name.toStdString()),
+ Napi::String::New(env(), defaultValue.toStdString()), Napi::String::New(env(), domainName.toStdString())});
+ }, defaultValue, name, domainName);
+}
+
+void QJsSettings::registerKeyObserver(const QString &name, const std::function<void()> &task, bool userDomain, QJsContext *context)
+{
+ QJsContext *_context = context;
+ if (_context == nullptr) {
+ _context = qNativeWindowManager->context();
+ return;
+ }
+ if (_context == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&](){
+ QString domainName = userDomain ? DomainName::USER_PROPERTY : DomainName::DEVICE_SHARED;
+ Napi::Function callback = Napi::Function::New(env(), [jsTask = std::move(task)](const Napi::CallbackInfo& info) {
+ jsTask();
+ });
+ call("registerKeyObserver", { _context->object(), Napi::String::New(env(), name.toStdString()),
+ Napi::String::New(env(), domainName.toStdString()), callback});
+ });
+}
+
+void QJsSettings::unregisterKeyObserver(const QString &name, bool userDomain, QJsContext *context)
+{
+ QJsContext *_context = context;
+ if (_context == nullptr) {
+ _context = qNativeWindowManager->context();
+ return;
+ }
+ if (_context == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&](){
+ QString domainName = userDomain ? DomainName::USER_PROPERTY : DomainName::DEVICE_SHARED;
+ call("unregisterKeyObserver", { _context->object(), Napi::String::New(env(), name.toStdString()),
+ Napi::String::New(env(), domainName.toStdString())});
+ });
+}
+
+QJsSettings *QJsSettings::instance()
+{
+ return settings();
+}
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,31 @@
+#ifndef QJSSETTINGS_H
+#define QJSSETTINGS_H
+
+#include <QString>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+class QJsContext;
+class QJsSettings : public QJsModule
+{
+public:
+ QJsSettings();
+ ~QJsSettings();
+
+ static QJsSettings *instance();
+
+ bool isFreeWindowEnable() const;
+
+ QString getValueFromUserDomain(const QString &name) const;
+
+ QString getValueFromSharedDomain(const QString &name) const;
+
+ void registerKeyObserver(const QString &name, const std::function<void()> &task, bool userDomain = true, QJsContext *context = nullptr);
+ void unregisterKeyObserver(const QString &name, bool userDomain = true, QJsContext *context = nullptr);
+private:
+ QString getValueSync(const QString &name, const QString &defaultValue, const QString &domainName) const;
+ mutable bool m_freeWindowEnable = false;
+};
+
+QT_END_NAMESPACE
+#endif // QJSSETTINGS_H
new file mode 100644
@@ -0,0 +1,35 @@
+#include "qjsuiability.h"
+#include "qohnativewindowmanager.h"
+#include "qjsuiabilitycontext.h"
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsUIAbility::QJsUIAbility(Napi::Object jsUIAbility)
+ : QJsAbility(jsUIAbility)
+{
+
+}
+
+QJsUIAbility::~QJsUIAbility()
+{
+ if (m_context != nullptr)
+ delete m_context;
+}
+
+QJsContext *QJsUIAbility::context() const
+{
+ if (m_context == nullptr) {
+ m_context = QtOh::runOnJsUIThreadWithResult([this]{
+ return new QJsUIAbilityContext(get("context").ToObject());
+ });
+ }
+ return m_context;
+}
+
+QJsAbility::AbilityType QJsUIAbility::abilityType() const
+{
+ return AbilityType::UIAbility;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,25 @@
+#ifndef QJSUIABILITY_H
+#define QJSUIABILITY_H
+
+#include <QByteArray>
+
+#include "qjsability.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsWant;
+class QJsUIAbilityContext;
+
+class QJsUIAbility : public QJsAbility
+{
+public:
+ QJsUIAbility(Napi::Object jsUIAbility);
+ virtual ~QJsUIAbility();
+
+ QJsContext *context() const override;
+
+ AbilityType abilityType() const override;
+};
+
+QT_END_NAMESPACE
+#endif // QJSUIABILITY_H
new file mode 100644
@@ -0,0 +1,34 @@
+#include "qjsabilityinfo.h"
+#include "qjsuiabilitycontext.h"
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsUIAbilityContext::QJsUIAbilityContext(Napi::Object jsObject)
+ : QJsContext(jsObject)
+ , m_abilityInfo(nullptr)
+{
+
+}
+
+QJsUIAbilityContext::~QJsUIAbilityContext()
+{
+
+}
+
+QJsAbilityInfo *QJsUIAbilityContext::abilityInfo()
+{
+ if (m_abilityInfo == nullptr) {
+ m_abilityInfo = QtOh::runOnJsUIThreadWithResult([this]{
+ return new QJsAbilityInfo(get("abilityInfo").ToObject());
+ });
+ }
+ return m_abilityInfo;
+}
+
+QJsContext::ContextType QJsUIAbilityContext::contextType() const
+{
+ return ContextType::UIContext;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QJSUIABILITYCONTEXT_H
+#define QJSUIABILITYCONTEXT_H
+
+#include "qjscontext.h"
+
+struct NativeResourceManager;
+
+QT_BEGIN_NAMESPACE
+
+class QJsWindowStage;
+class QJsAbilityInfo;
+class QJsApplicationContext;
+
+class QJsUIAbilityContext : public QJsContext
+{
+public:
+ QJsUIAbilityContext(Napi::Object jsObject);
+ ~QJsUIAbilityContext();
+
+ QJsAbilityInfo *abilityInfo();
+
+ ContextType contextType() const override;
+private:
+ QJsAbilityInfo *m_abilityInfo;
+};
+
+QT_END_NAMESPACE
+#endif // QJSUIABILITYCONTEXT_H
new file mode 100644
@@ -0,0 +1,43 @@
+#include <QtCore/qopenharmonydefines.h>
+
+#include "qjsuiextensionability.h"
+#include "qjsuiextensioncontext.h"
+#include "qjsuiextensioncontextsession.h"
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsUIExtensionAbility::QJsUIExtensionAbility(Napi::Object jsUIAbility, Napi::Object session)
+ : QJsAbility(jsUIAbility),
+ m_session(new QJsUIExtensionContextSession(session))
+{
+
+}
+
+QJsUIExtensionAbility::~QJsUIExtensionAbility()
+{
+ if (m_context != nullptr)
+ delete m_context;
+}
+
+QJsContext *QJsUIExtensionAbility::context() const
+{
+ if (m_context == nullptr) {
+ m_context = QtOh::runOnJsUIThreadWithResult([this]{
+ return new QJsUIExtensionContext(get("context").ToObject());
+ });
+ }
+ return m_context;
+}
+
+QJsAbility::AbilityType QJsUIExtensionAbility::abilityType() const
+{
+ return AbilityType::UIExtensionAbility;
+}
+
+QJsUIExtensionContextSession *QJsUIExtensionAbility::session() const
+{
+ return m_session;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,26 @@
+#ifndef QJSUIEXTENSIONABILITY_H
+#define QJSUIEXTENSIONABILITY_H
+
+#include "qjsability.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsUIExtensionContextSession;
+
+class QJsUIExtensionAbility : public QJsAbility
+{
+public:
+ QJsUIExtensionAbility(Napi::Object jsUIAbility, Napi::Object session);
+ virtual ~QJsUIExtensionAbility();
+
+ QJsContext *context() const override;
+
+ AbilityType abilityType() const override;
+
+ QJsUIExtensionContextSession* session() const;
+private:
+ mutable QJsUIExtensionContextSession* m_session = nullptr;
+};
+
+QT_END_NAMESPACE
+#endif // QJSUIEXTENSIONABILITY_H
new file mode 100644
@@ -0,0 +1,21 @@
+#include "qjsuiextensioncontext.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsUIExtensionContext::QJsUIExtensionContext(Napi::Object jsObject)
+ : QJsContext(jsObject)
+{
+
+}
+
+QJsUIExtensionContext::~QJsUIExtensionContext()
+{
+
+}
+
+QJsContext::ContextType QJsUIExtensionContext::contextType() const
+{
+ return ContextType::UIExtensionContext;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,18 @@
+#ifndef QJSUIEXTENSIONCONTEXT_H
+#define QJSUIEXTENSIONCONTEXT_H
+
+#include "qjscontext.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsUIExtensionContext : public QJsContext
+{
+public:
+ QJsUIExtensionContext(Napi::Object jsObject);
+ ~QJsUIExtensionContext();
+
+ ContextType contextType() const override;
+};
+
+QT_END_NAMESPACE
+#endif // QJSUIEXTENSIONCONTEXT_H
new file mode 100644
@@ -0,0 +1,19 @@
+#include "qjsuiextensioncontextsession.h"
+#include <QtCore/qopenharmonydefines.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsUIExtensionContextSession::QJsUIExtensionContextSession(Napi::Object jsObject)
+ : QJsWindowProxy(jsObject)
+{
+}
+
+QJsUIExtensionContextSession::~QJsUIExtensionContextSession() { }
+
+Napi::Object QJsUIExtensionContextSession::mainwindow()
+{
+ Napi::Value result = call("getUIExtensionWindowProxy");
+ return result.As<Napi::Object>();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,19 @@
+#ifndef QJSUIEXTENSIONCONTEXTSESSION_H
+#define QJSUIEXTENSIONCONTEXTSESSION_H
+
+#include "qjswindowproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsWindowStage;
+class QJsUIExtensionContextSession : public QJsWindowProxy
+{
+public:
+ QJsUIExtensionContextSession(Napi::Object jsObject);
+ ~QJsUIExtensionContextSession();
+
+ Napi::Object mainwindow() override;
+};
+
+QT_END_NAMESPACE
+#endif // QJSUIEXTENSIONCONTEXTSESSION_H
new file mode 100644
@@ -0,0 +1,152 @@
+#include "qjswant.h"
+#include <qnapi.h>
+#include <private/qopenharmony_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QString QJsWant::defaultWindowName = "qt_entry_window";
+
+QJsWant::QJsWant(Napi::Object jsObject)
+ : QJsObject(jsObject)
+{
+
+}
+
+QJsWant::~QJsWant()
+{
+
+}
+
+bool QJsWant::isSilentStart() const
+{
+ if (!object().Has("parameters"))
+ return false;
+
+ auto jsParameters = get("parameters");
+ return QNapi::getOrDefault<bool>(jsParameters, "ability.param.extra.isSilentStart", false);
+}
+
+QString QJsWant::bundleName() const
+{
+ return QString::fromStdString(get("bundleName").ToString());
+}
+
+QString QJsWant::abilityName() const
+{
+ return QString::fromStdString(get("abilityName").ToString());
+}
+
+void QJsWant::saveParamsToEnv()
+{
+ auto setEnv = [](const char *name, const QByteArray &value) {
+ if (::setenv(name, value.constData(), 1) != 0) {
+ LOGE("Can't set environment for %{public}s", name);
+ } else {
+ LOGI("Set environment for %{public}s", name);
+ }
+ };
+ if (object().Has("parameters")) {
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ auto keys = jsParameters.GetPropertyNames();
+ for (uint32_t i = 0; i < keys.Length(); ++i) {
+ Napi::Value key = keys.Get(i);
+ Napi::Value value = jsParameters.Get(key);
+ std::string keyStr = key.As<Napi::String>();
+ setEnv(keyStr.data(), QByteArray::fromStdString(value.ToString()));
+ }
+ }
+}
+
+static inline QByteArray pathFromUri(const QByteArray &uri)
+{
+ return QtOh::pathFromUri(QString::fromUtf8(uri)).toUtf8();
+}
+
+QList<QByteArray> QJsWant::launchParams() const
+{
+ QList<QByteArray> result;
+ auto u = uri();
+ if (!u.isEmpty()) {
+ result << pathFromUri(u);
+ }
+
+ if (object().Has("parameters")) {
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ auto keys = jsParameters.GetPropertyNames();
+ for (uint32_t i = 0; i < keys.Length(); ++i) {
+ Napi::Value key = keys.Get(i);
+ Napi::Value value = jsParameters.Get(key);
+ std::string keyStr = key.As<Napi::String>();
+ if ((keyStr == "uriList" || keyStr == "ability.params.stream") && value.IsArray()) {
+ Napi::Array uriLists = value.As<Napi::Array>();
+ for (uint32_t j = 0; j < uriLists.Length(); ++j) {
+ Napi::Value uri = uriLists.Get(j);
+ if (uri.IsString()) {
+ auto u = QByteArray::fromStdString(uri.ToString());
+ if (u.isEmpty())
+ continue;
+ auto path = pathFromUri(u);
+ if (result.contains(path))
+ continue;
+ result << path;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+QString QJsWant::moduleName() const
+{
+ return QString::fromStdString(get("moduleName").ToString());
+}
+
+QByteArray QJsWant::uri() const
+{
+ QByteArray uri = QByteArray::fromStdString(get("uri").ToString());
+ return uri;
+}
+
+QString QJsWant::windowName() const
+{
+ if (!object().Has("parameters"))
+ return defaultWindowName;
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ Napi::Value ret = jsParameters.Get("windowName");
+ if (ret.IsUndefined() || ret.IsEmpty())
+ return defaultWindowName;
+
+ return QString::fromStdString(ret.ToString());
+}
+
+bool QJsWant::hasWindowName() const
+{
+ if (!object().Has("parameters"))
+ return false;
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ return jsParameters.Has("windowName");
+}
+
+bool QJsWant::hasProcessIdentifier() const
+{
+ if (!object().Has("parameters"))
+ return false;
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ return jsParameters.Has("processIdentifier");
+}
+
+QString QJsWant::processIdentifier() const
+{
+ if (!object().Has("parameters"))
+ return QString();
+ Napi::Object jsParameters = get("parameters").As<Napi::Object>();
+ Napi::Value ret = jsParameters.Get("processIdentifier");
+ if (ret.IsUndefined() || ret.IsEmpty())
+ return QString();
+
+ return QString::fromStdString(ret.ToString());
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,40 @@
+#ifndef QJSWANT_H
+#define QJSWANT_H
+
+#include <qjsobject.h>
+
+#include <QString>
+#include <QList>
+
+class QJsWant : public QJsObject
+{
+public:
+ QJsWant(Napi::Object jsObject);
+ ~QJsWant();
+
+ bool isSilentStart() const;
+
+ QString bundleName() const;
+
+ QString abilityName() const;
+
+ QString moduleName() const;
+
+ QString windowName() const;
+
+ bool hasWindowName() const;
+
+ bool hasProcessIdentifier() const;
+
+ QString processIdentifier() const;
+
+ QByteArray uri() const;
+
+ QList<QByteArray> launchParams() const;
+
+ void saveParamsToEnv();
+
+ static QString defaultWindowName;
+};
+
+#endif // QJSWANT_H
new file mode 100644
@@ -0,0 +1,16 @@
+#include "qjswindowproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+QJsWindowProxy::QJsWindowProxy(Napi::Object windowproxy)
+ : QJsObject(windowproxy)
+{
+
+}
+
+QJsWindowProxy::~QJsWindowProxy()
+{
+
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,18 @@
+#ifndef QJSWINDOWPROXY_H
+#define QJSWINDOWPROXY_H
+
+#include <QJsObject>
+
+QT_BEGIN_NAMESPACE
+
+class QJsWindowProxy : public QJsObject
+{
+public:
+ QJsWindowProxy(Napi::Object windowproxy);
+ ~QJsWindowProxy();
+
+ virtual Napi::Object mainwindow() = 0;
+};
+
+QT_END_NAMESPACE
+#endif // QJSWINDOWPROXY_H
new file mode 100644
@@ -0,0 +1,49 @@
+#include "qjswindowstage.h"
+#include "qohmain.h"
+#include "qohauxiliary.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QJsWindowStage::QJsWindowStage(Napi::Object jsObject)
+ : QJsWindowProxy(jsObject)
+{
+
+}
+
+QJsWindowStage::~QJsWindowStage()
+{
+
+}
+
+Napi::Object QJsWindowStage::mainwindow()
+{
+ Napi::Value result = call("getMainWindowSync");
+ return result.As<Napi::Object>();
+}
+
+bool QJsWindowStage::isWindowRectAutoSave()
+{
+ if (!QtOh::checkDeviceType(QtOh::Utility::PC)) {
+ qCWarning(openharmonyQPA, "isWindowRectAutoSave This interface is not support on this device");
+ return false;
+ }
+
+ return QtOh::runOnJsUIThreadWithPromise<bool>([this](auto p) {
+ Napi::Value ret = call("isWindowRectAutoSave");
+ if (ret.IsEmpty() || ret.IsNull()) {
+ p->set_value(false);
+ return;
+ }
+
+ QJsPromise promise(ret.As<Napi::Promise>());
+ promise.onThen([this, p](const Napi::CallbackInfo &info) {
+ bool isAutoSave = info[0].ToBoolean();
+ p->set_value(isAutoSave);
+ }).onCatch([this, p](const Napi::CallbackInfo &) {
+ p->set_value(false);
+ });
+ });
+}
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,20 @@
+#ifndef QJSWINDOWSTAGE_H
+#define QJSWINDOWSTAGE_H
+
+#include "qjswindowproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+class QJsWindowStage : public QJsWindowProxy
+{
+public:
+ QJsWindowStage(Napi::Object jsObject);
+ ~QJsWindowStage();
+
+ Napi::Object mainwindow() override;
+
+ bool isWindowRectAutoSave();
+};
+
+QT_END_NAMESPACE
+#endif // QJSWINDOWSTAGE_H
new file mode 100644
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "openharmony" ]
+}
new file mode 100644
@@ -0,0 +1,136 @@
+TARGET = qopenharmony
+
+CONFIG += c++latest
+
+greaterThan(OHOS_SDK_VERSION, 14) {
+ LIBS += -lnative_window_manager
+}
+
+QT += \
+ core-private gui-private egl_support-private \
+ eventdispatcher_support-private fontdatabase_support-private accessibility_support-private \
+ theme_support-private
+
+LIBS += -lEGL -lace_napi.z -lohfileuri -lace_ndk.z -lrawfile.z -lnative_window \
+ -ludmf -lpixelmap -lpasteboard -lohinput -lnative_display_manager -lqos -lhitrace_ndk.z \
+ -lnative_drawing -lnative_window_manager -lohinputmethod -lnative_vsync -limage_source -lohresmgr
+
+
+qtConfig(vulkan){
+ QT += vulkan_support-private
+
+ HEADERS += $$PWD/qohplatformvulkaninstance.h
+
+ SOURCES += $$PWD/qohplatformvulkaninstance.cpp
+}
+
+OTHER_FILES += $$PWD/openharmony.json
+
+HEADERS += $$PWD/qohplatformintegration.h \
+ $$PWD/qohplatformwindow.h \
+ $$PWD/qohdesktopwindow.h \
+ $$PWD/qohplatformopenglwindow.h \
+ $$PWD/qohplatformforeignwindow.h \
+ $$PWD/qohplatformscreen.h \
+ $$PWD/qohplatformbackingstore.h \
+ $$PWD/qohxcomponent.h \
+ $$PWD/qohnativewindowmanager.h \
+ $$PWD/qohplatformopenglcontext.h \
+ $$PWD/qoheventdispatcher.h \
+ $$PWD/qohplatformoffscreensurface.h \
+ $$PWD/qohmain.h \
+ $$PWD/qohplatforminputcontext.h \
+ $$PWD/qohplatformtheme.h \
+ $$PWD/qohplatformdialoghelpers.h \
+ $$PWD/qohplatformclipboard.h \
+ $$PWD/qohdrag.h \
+ $$PWD/qohfileenginehandler.h \
+ $$PWD/qohnativewindow.h \
+ $$PWD/qohjsonlistener.h \
+ $$PWD/qohwindownode.h \
+ $$PWD/qohwindownodeeventhandler.h \
+ $$PWD/qohplatformservices.h \
+ $$PWD/qohplatformcursor.h \
+ $$PWD/qohplatformabilityctrl.h \
+ $$PWD/qohplatformnativeinterface.h \
+ $$PWD/qohevent.h \
+ $$PWD/qohextensionwindow.h \
+ $$PWD/qohnormalwindow.h \
+ $$PWD/qohauxiliary.h \
+ $$PWD/qohsystemtrayicon.h \
+ $$PWD/qohplatformmenu.h \
+ $$PWD/qohobjectholder.h \
+ $$PWD/qohnativenodeapi.h \
+ $$PWD/qoharkui.h \
+ $$PWD/qohwindowcontext.h \
+ $$PWD/qohkeys.h
+
+SOURCES += $$PWD/qohplatformplugin.cpp \
+ $$PWD/qohplatformwindow.cpp \
+ $$PWD/qohdesktopwindow.cpp \
+ $$PWD/qohplatformintegration.cpp \
+ $$PWD/qohplatformopenglwindow.cpp \
+ $$PWD/qohplatformforeignwindow.cpp \
+ $$PWD/qohplatformscreen.cpp \
+ $$PWD/qohplatformbackingstore.cpp \
+ $$PWD/qohplatformopenglcontext.cpp \
+ $$PWD/qohplatformoffscreensurface.cpp \
+ $$PWD/qohmain.cpp \
+ $$PWD/qohxcomponent.cpp \
+ $$PWD/qohnativewindowmanager.cpp \
+ $$PWD/qoheventdispatcher.cpp \
+ $$PWD/qohplatforminputcontext.cpp \
+ $$PWD/qohplatformtheme.cpp \
+ $$PWD/qohplatformdialoghelpers.cpp \
+ $$PWD/qohplatformclipboard.cpp \
+ $$PWD/qohdrag.cpp \
+ $$PWD/qohfileenginehandler.cpp \
+ $$PWD/qohnativewindow.cpp \
+ $$PWD/qohjsonlistener.cpp \
+ $$PWD/qohwindownode.cpp \
+ $$PWD/qohwindownodeeventhandler.cpp \
+ $$PWD/qohplatformservices.cpp \
+ $$PWD/qohplatformcursor.cpp \
+ $$PWD/qohplatformabilityctrl.cpp \
+ $$PWD/qohplatformnativeinterface.cpp \
+ $$PWD/qohextensionwindow.cpp \
+ $$PWD/qohnormalwindow.cpp \
+ $$PWD/qohauxiliary.cpp \
+ $$PWD/qohsystemtrayicon.cpp \
+ $$PWD/qohplatformmenu.cpp \
+ $$PWD/qohnativenodeapi.cpp \
+ $$PWD/qoharkui.cpp \
+ $$PWD/qohwindowcontext.cpp \
+ $$PWD/qohkeys.cpp
+
+greaterThan(OHOS_SDK_VERSION, 13) {
+ include(accessibility/accessibility.pri)
+ HEADERS += \
+ $$PWD/qohdisplay.h \
+ $$PWD/qohdisplaymanager.h \
+ $$PWD/qohdisplaylistener.h
+
+ SOURCES += \
+ $$PWD/qohdisplay.cpp \
+ $$PWD/qohdisplaymanager.cpp \
+ $$PWD/qohdisplaylistener.cpp
+}
+
+greaterThan(OHOS_SDK_VERSION, 14) {
+ HEADERS += \
+ $$PWD/qohnativevsync.h
+
+ SOURCES += \
+ $$PWD/qohnativevsync.cpp
+}
+
+include(jsclass/jsclass.pri)
+include(inputmethod/inputmethod.pri)
+include(arkui/arkui.pri)
+PLUGIN_TYPE = platforms
+
+PLUGIN_CLASS_NAME = QOpenHarmonyPlatformIntegrationPlugin
+!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
+
+load(qt_plugin)
+
new file mode 100644
@@ -0,0 +1,470 @@
+#include "qoharkui.h"
+#include <QtCore/QHash>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtOhArkUI {
+
+static QHash<int32_t, QString> errors = {
+ {ARKUI_ERROR_CODE_NO_ERROR, "No errors"},
+ {ARKUI_ERROR_CODE_PARAM_INVALID, "Parameter error"},
+#if OHOS_SDK_VERSION > 14
+ {ARKUI_ERROR_CODE_INTERNAL_ERROR, "Internal error occurs"},
+#endif
+ {ARKUI_ERROR_CODE_ATTRIBUTE_OR_EVENT_NOT_SUPPORTED, "The component does not support specific properties or events"},
+ {ARKUI_ERROR_CODE_ARKTS_NODE_NOT_SUPPORTED, "The corresponding operation does not support nodes created by ArkTS"},
+ {ARKUI_ERROR_CODE_ADAPTER_NOT_BOUND, "The lazy loading adapter is not bound to the component"},
+ {ARKUI_ERROR_CODE_ADAPTER_EXIST, "The adapter already exists"},
+ {ARKUI_ERROR_CODE_CHILD_NODE_EXIST, "The corresponding node already has a child node and cannot add an adapter"},
+ {ARKUI_ERROR_CODE_NODE_EVENT_PARAM_INDEX_OUT_OF_RANGE, "The parameter length in the parameter event exceeds the limit"},
+ {ARKUI_ERROR_CODE_NODE_EVENT_PARAM_INVALID, "The data does not exist in the component event"},
+ {ARKUI_ERROR_CODE_NODE_EVENT_NO_RETURN, "The component event does not support return values"},
+ {ARKUI_ERROR_CODE_NODE_INDEX_INVALID, "The index value is invalid"},
+ {ARKUI_ERROR_CODE_GET_INFO_FAILED, "Failed to query route navigation information"},
+ {ARKUI_ERROR_CODE_BUFFER_SIZE_ERROR, "The buffer size is not large enough"},
+#if OHOS_SDK_VERSIOIN > 14
+ {ARKUI_ERROR_CODE_NODE_NOT_ON_MAIN_TREE, "The node is not on main tree"},
+ {ARKUI_ERROR_CODE_FOCUS_NON_FOCUSABLE, "The node requesting focus is not focusable"},
+ {ARKUI_ERROR_CODE_FOCUS_NON_FOCUSABLE_ANCESTOR, "The node requesting focus has unfocusable ancestor"},
+ {ARKUI_ERROR_CODE_FOCUS_NON_EXISTENT, "The node requesting focus does not exists"},
+ {ARKUI_ERROR_CODE_COMPONENT_SNAPSHOT_TIMEOUT, "The snapshot taking is timeout"},
+#endif
+ {ARKUI_ERROR_CODE_NON_SCROLLABLE_CONTAINER, "The component is not a scroll container"},
+ {ARKUI_ERROR_CODE_BUFFER_SIZE_NOT_ENOUGH, "The buffer is not large enough"},
+#if OHOS_SDK_VERSIOIN > 14
+ {ARKUI_ERROR_CODE_NOT_CLONED_POINTER_EVENT, "The event is not a clone event"},
+ {ARKUI_ERROR_CODE_POST_CLONED_COMPONENT_STATUS_ABNORMAL, "The component status is abnormal"},
+ {ARKUI_ERROR_CODE_POST_CLONED_NO_COMPONENT_HIT_TO_RESPOND_TO_THE_EVENT, "No component hit to respond to the event"},
+#endif
+ {ARKUI_ERROR_CODE_INVALID_STYLED_STRING, "invalid styled string"}
+};
+
+QString ErrorCode::toString(int32_t code)
+{
+ return errors.value(code, "Unknow error");
+}
+
+void ErrorCode::printErrorCode(const QString &message, int32_t code)
+{
+ qWarning() << QString("%1---code is: %2, error string is: %3").arg(message).arg(code).arg(toString(code));
+}
+
+#define MAP_ENUM(ENUM) ENUM, #ENUM
+
+constexpr std::array NodeAttributeTypeMap = {
+ std::pair{MAP_ENUM(NODE_WIDTH)},
+ std::pair{MAP_ENUM(NODE_HEIGHT)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_COLOR)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_IMAGE)},
+ std::pair{MAP_ENUM(NODE_PADDING)},
+ std::pair{MAP_ENUM(NODE_ID)},
+ std::pair{MAP_ENUM(NODE_ENABLED)},
+ std::pair{MAP_ENUM(NODE_MARGIN)},
+ std::pair{MAP_ENUM(NODE_TRANSLATE)},
+ std::pair{MAP_ENUM(NODE_SCALE)},
+ std::pair{MAP_ENUM(NODE_ROTATE)},
+ std::pair{MAP_ENUM(NODE_BRIGHTNESS)},
+ std::pair{MAP_ENUM(NODE_SATURATION)},
+ std::pair{MAP_ENUM(NODE_BLUR)},
+ std::pair{MAP_ENUM(NODE_LINEAR_GRADIENT)},
+ std::pair{MAP_ENUM(NODE_ALIGNMENT)},
+ std::pair{MAP_ENUM(NODE_OPACITY)},
+ std::pair{MAP_ENUM(NODE_BORDER_WIDTH)},
+ std::pair{MAP_ENUM(NODE_BORDER_RADIUS)},
+ std::pair{MAP_ENUM(NODE_BORDER_COLOR)},
+ std::pair{MAP_ENUM(NODE_BORDER_STYLE)},
+ std::pair{MAP_ENUM(NODE_Z_INDEX)},
+ std::pair{MAP_ENUM(NODE_VISIBILITY)},
+ std::pair{MAP_ENUM(NODE_CLIP)},
+ std::pair{MAP_ENUM(NODE_CLIP_SHAPE)},
+ std::pair{MAP_ENUM(NODE_TRANSFORM)},
+ std::pair{MAP_ENUM(NODE_HIT_TEST_BEHAVIOR)},
+ std::pair{MAP_ENUM(NODE_POSITION)},
+ std::pair{MAP_ENUM(NODE_SHADOW)},
+ std::pair{MAP_ENUM(NODE_CUSTOM_SHADOW)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_IMAGE_SIZE)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_IMAGE_SIZE_WITH_STYLE)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_BLUR_STYLE)},
+ std::pair{MAP_ENUM(NODE_TRANSFORM_CENTER)},
+ std::pair{MAP_ENUM(NODE_OPACITY_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_ROTATE_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_SCALE_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_TRANSLATE_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_MOVE_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_FOCUSABLE)},
+ std::pair{MAP_ENUM(NODE_DEFAULT_FOCUS)},
+ std::pair{MAP_ENUM(NODE_RESPONSE_REGION)},
+ std::pair{MAP_ENUM(NODE_OVERLAY)},
+ std::pair{MAP_ENUM(NODE_SWEEP_GRADIENT)},
+ std::pair{MAP_ENUM(NODE_RADIAL_GRADIENT)},
+ std::pair{MAP_ENUM(NODE_MASK)},
+ std::pair{MAP_ENUM(NODE_BLEND_MODE)},
+ std::pair{MAP_ENUM(NODE_DIRECTION)},
+ std::pair{MAP_ENUM(NODE_CONSTRAINT_SIZE)},
+ std::pair{MAP_ENUM(NODE_GRAY_SCALE)},
+ std::pair{MAP_ENUM(NODE_INVERT)},
+ std::pair{MAP_ENUM(NODE_SEPIA)},
+ std::pair{MAP_ENUM(NODE_CONTRAST)},
+ std::pair{MAP_ENUM(NODE_FOREGROUND_COLOR)},
+ std::pair{MAP_ENUM(NODE_OFFSET)},
+ std::pair{MAP_ENUM(NODE_MARK_ANCHOR)},
+ std::pair{MAP_ENUM(NODE_BACKGROUND_IMAGE_POSITION)},
+ std::pair{MAP_ENUM(NODE_ALIGN_RULES)},
+ std::pair{MAP_ENUM(NODE_ALIGN_SELF)},
+ std::pair{MAP_ENUM(NODE_FLEX_GROW)},
+ std::pair{MAP_ENUM(NODE_FLEX_SHRINK)},
+ std::pair{MAP_ENUM(NODE_FLEX_BASIS)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_GROUP)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_TEXT)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_MODE)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_DESCRIPTION)},
+ std::pair{MAP_ENUM(NODE_FOCUS_STATUS)},
+ std::pair{MAP_ENUM(NODE_ASPECT_RATIO)},
+ std::pair{MAP_ENUM(NODE_LAYOUT_WEIGHT)},
+ std::pair{MAP_ENUM(NODE_DISPLAY_PRIORITY)},
+ std::pair{MAP_ENUM(NODE_OUTLINE_WIDTH)},
+ std::pair{MAP_ENUM(NODE_WIDTH_PERCENT)},
+ std::pair{MAP_ENUM(NODE_HEIGHT_PERCENT)},
+ std::pair{MAP_ENUM(NODE_PADDING_PERCENT)},
+ std::pair{MAP_ENUM(NODE_MARGIN_PERCENT)},
+ std::pair{MAP_ENUM(NODE_GEOMETRY_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_RELATIVE_LAYOUT_CHAIN_MODE)},
+ std::pair{MAP_ENUM(NODE_RENDER_FIT)},
+ std::pair{MAP_ENUM(NODE_OUTLINE_COLOR)},
+ std::pair{MAP_ENUM(NODE_SIZE)},
+ std::pair{MAP_ENUM(NODE_RENDER_GROUP)},
+ std::pair{MAP_ENUM(NODE_COLOR_BLEND)},
+ std::pair{MAP_ENUM(NODE_FOREGROUND_BLUR_STYLE)},
+ std::pair{MAP_ENUM(NODE_LAYOUT_RECT)},
+ std::pair{MAP_ENUM(NODE_FOCUS_ON_TOUCH)},
+ std::pair{MAP_ENUM(NODE_BORDER_WIDTH_PERCENT)},
+ std::pair{MAP_ENUM(NODE_BORDER_RADIUS_PERCENT)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_ID)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_ACTIONS)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_ROLE)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_STATE)},
+ std::pair{MAP_ENUM(NODE_ACCESSIBILITY_VALUE)},
+ std::pair{MAP_ENUM(NODE_EXPAND_SAFE_AREA)},
+ std::pair{MAP_ENUM(NODE_VISIBLE_AREA_CHANGE_RATIO)},
+ std::pair{MAP_ENUM(NODE_TRANSITION)},
+ std::pair{MAP_ENUM(NODE_UNIQUE_ID)},
+ std::pair{MAP_ENUM(NODE_FOCUS_BOX)},
+ std::pair{MAP_ENUM(NODE_CLICK_DISTANCE)},
+ std::pair{MAP_ENUM(NODE_TAB_STOP)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_BACKDROP_BLUR)},
+#endif
+ std::pair{MAP_ENUM(NODE_TEXT_CONTENT)},
+ std::pair{MAP_ENUM(NODE_FONT_COLOR)},
+ std::pair{MAP_ENUM(NODE_FONT_SIZE)},
+ std::pair{MAP_ENUM(NODE_FONT_STYLE)},
+ std::pair{MAP_ENUM(NODE_FONT_WEIGHT)},
+ std::pair{MAP_ENUM(NODE_TEXT_LINE_HEIGHT)},
+ std::pair{MAP_ENUM(NODE_TEXT_DECORATION)},
+ std::pair{MAP_ENUM(NODE_TEXT_CASE)},
+ std::pair{MAP_ENUM(NODE_TEXT_LETTER_SPACING)},
+ std::pair{MAP_ENUM(NODE_TEXT_MAX_LINES)},
+ std::pair{MAP_ENUM(NODE_TEXT_ALIGN)},
+ std::pair{MAP_ENUM(NODE_TEXT_OVERFLOW)},
+ std::pair{MAP_ENUM(NODE_FONT_FAMILY)},
+ std::pair{MAP_ENUM(NODE_TEXT_COPY_OPTION)},
+ std::pair{MAP_ENUM(NODE_TEXT_BASELINE_OFFSET)},
+ std::pair{MAP_ENUM(NODE_TEXT_TEXT_SHADOW)},
+ std::pair{MAP_ENUM(NODE_TEXT_MIN_FONT_SIZE)},
+ std::pair{MAP_ENUM(NODE_TEXT_MAX_FONT_SIZE)},
+ std::pair{MAP_ENUM(NODE_TEXT_FONT)},
+ std::pair{MAP_ENUM(NODE_TEXT_HEIGHT_ADAPTIVE_POLICY)},
+ std::pair{MAP_ENUM(NODE_TEXT_INDENT)},
+ std::pair{MAP_ENUM(NODE_TEXT_WORD_BREAK)},
+ std::pair{MAP_ENUM(NODE_TEXT_ELLIPSIS_MODE)},
+ std::pair{MAP_ENUM(NODE_TEXT_LINE_SPACING)},
+ std::pair{MAP_ENUM(NODE_FONT_FEATURE)},
+ std::pair{MAP_ENUM(NODE_TEXT_ENABLE_DATA_DETECTOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_ENABLE_DATA_DETECTOR_CONFIG)},
+ std::pair{MAP_ENUM(NODE_TEXT_SELECTED_BACKGROUND_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_CONTENT_WITH_STYLED_STRING)},
+ std::pair{MAP_ENUM(NODE_TEXT_HALF_LEADING)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_IMMUTABLE_FONT_WEIGHT)},
+#endif
+ std::pair{MAP_ENUM(NODE_SPAN_CONTENT)},
+ std::pair{MAP_ENUM(NODE_SPAN_TEXT_BACKGROUND_STYLE)},
+ std::pair{MAP_ENUM(NODE_SPAN_BASELINE_OFFSET)},
+ std::pair{MAP_ENUM(NODE_IMAGE_SPAN_SRC)},
+ std::pair{MAP_ENUM(NODE_IMAGE_SPAN_VERTICAL_ALIGNMENT)},
+ std::pair{MAP_ENUM(NODE_IMAGE_SPAN_ALT)},
+ std::pair{MAP_ENUM(NODE_IMAGE_SPAN_BASELINE_OFFSET)},
+ std::pair{MAP_ENUM(NODE_IMAGE_SRC)},
+ std::pair{MAP_ENUM(NODE_IMAGE_OBJECT_FIT)},
+ std::pair{MAP_ENUM(NODE_IMAGE_INTERPOLATION)},
+ std::pair{MAP_ENUM(NODE_IMAGE_OBJECT_REPEAT)},
+ std::pair{MAP_ENUM(NODE_IMAGE_COLOR_FILTER)},
+ std::pair{MAP_ENUM(NODE_IMAGE_AUTO_RESIZE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ALT)},
+ std::pair{MAP_ENUM(NODE_IMAGE_DRAGGABLE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_RENDER_MODE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_FIT_ORIGINAL_SIZE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_FILL_COLOR)},
+ std::pair{MAP_ENUM(NODE_IMAGE_RESIZABLE)},
+ std::pair{MAP_ENUM(NODE_TOGGLE_SELECTED_COLOR)},
+ std::pair{MAP_ENUM(NODE_TOGGLE_SWITCH_POINT_COLOR)},
+ std::pair{MAP_ENUM(NODE_TOGGLE_VALUE)},
+ std::pair{MAP_ENUM(NODE_TOGGLE_UNSELECTED_COLOR)},
+ std::pair{MAP_ENUM(NODE_LOADING_PROGRESS_COLOR)},
+ std::pair{MAP_ENUM(NODE_LOADING_PROGRESS_ENABLE_LOADING)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_PLACEHOLDER)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_TEXT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CARET_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CARET_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SHOW_UNDERLINE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_MAX_LENGTH)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_ENTER_KEY_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_PLACEHOLDER_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_PLACEHOLDER_FONT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_ENABLE_KEYBOARD_ON_FOCUS)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SELECTED_BACKGROUND_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SHOW_PASSWORD_ICON)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_EDITING)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CANCEL_BUTTON)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_TEXT_SELECTION)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_UNDERLINE_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_ENABLE_AUTO_FILL)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CONTENT_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_PASSWORD_RULES)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SELECT_ALL)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_INPUT_FILTER)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CARET_OFFSET)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CONTENT_RECT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CONTENT_LINE_COUNT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SELECTION_MENU_HIDDEN)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_BLUR_ON_SUBMIT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_CUSTOM_KEYBOARD)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_WORD_BREAK)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_SHOW_KEYBOARD_ON_FOCUS)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_NUMBER_OF_LINES)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_LETTER_SPACING)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_ENABLE_PREVIEW_TEXT)},
+ std::pair{MAP_ENUM(NODE_TEXT_INPUT_KEYBOARD_APPEARANCE)},
+#endif
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_PLACEHOLDER)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_TEXT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_MAX_LENGTH)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_PLACEHOLDER_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_PLACEHOLDER_FONT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_CARET_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_EDITING)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_SHOW_COUNTER)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_SELECTION_MENU_HIDDEN)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_BLUR_ON_SUBMIT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_INPUT_FILTER)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_SELECTED_BACKGROUND_COLOR)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_ENTER_KEY_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_ENABLE_KEYBOARD_ON_FOCUS)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_CARET_OFFSET)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_CONTENT_RECT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_CONTENT_LINE_COUNT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_TEXT_SELECTION)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_ENABLE_AUTO_FILL)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_CONTENT_TYPE)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_SHOW_KEYBOARD_ON_FOCUS)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_NUMBER_OF_LINES)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_LETTER_SPACING)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_ENABLE_PREVIEW_TEXT)},
+ std::pair{MAP_ENUM(NODE_TEXT_AREA_KEYBOARD_APPEARANCE)},
+#endif
+ std::pair{MAP_ENUM(NODE_BUTTON_LABEL)},
+ std::pair{MAP_ENUM(NODE_BUTTON_TYPE)},
+ std::pair{MAP_ENUM(NODE_PROGRESS_VALUE)},
+ std::pair{MAP_ENUM(NODE_PROGRESS_TOTAL)},
+ std::pair{MAP_ENUM(NODE_PROGRESS_COLOR)},
+ std::pair{MAP_ENUM(NODE_PROGRESS_TYPE)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_PROGRESS_LINEAR_STYLE)},
+#endif
+ std::pair{MAP_ENUM(NODE_CHECKBOX_SELECT)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_SELECT_COLOR)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_UNSELECT_COLOR)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_MARK)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_SHAPE)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_CHECKBOX_NAME)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP)},
+#endif
+ std::pair{MAP_ENUM(NODE_XCOMPONENT_ID)},
+ std::pair{MAP_ENUM(NODE_XCOMPONENT_TYPE)},
+ std::pair{MAP_ENUM(NODE_XCOMPONENT_SURFACE_SIZE)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_LUNAR)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_START)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_END)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_SELECTED)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_DISAPPEAR_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_DATE_PICKER_SELECTED_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TIME_PICKER_SELECTED)},
+ std::pair{MAP_ENUM(NODE_TIME_PICKER_USE_MILITARY_TIME)},
+ std::pair{MAP_ENUM(NODE_TIME_PICKER_DISAPPEAR_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TIME_PICKER_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TIME_PICKER_SELECTED_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_OPTION_RANGE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_OPTION_SELECTED)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_OPTION_VALUE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_DISAPPEAR_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_SELECTED_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_SELECTED_INDEX)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_CAN_LOOP)},
+ std::pair{MAP_ENUM(NODE_TEXT_PICKER_DEFAULT_PICKER_ITEM_HEIGHT)},
+ std::pair{MAP_ENUM(NODE_CALENDAR_PICKER_HINT_RADIUS)},
+ std::pair{MAP_ENUM(NODE_CALENDAR_PICKER_SELECTED_DATE)},
+ std::pair{MAP_ENUM(NODE_CALENDAR_PICKER_EDGE_ALIGNMENT)},
+ std::pair{MAP_ENUM(NODE_CALENDAR_PICKER_TEXT_STYLE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_BLOCK_COLOR)},
+ std::pair{MAP_ENUM(NODE_SLIDER_TRACK_COLOR)},
+ std::pair{MAP_ENUM(NODE_SLIDER_SELECTED_COLOR)},
+ std::pair{MAP_ENUM(NODE_SLIDER_SHOW_STEPS)},
+ std::pair{MAP_ENUM(NODE_SLIDER_BLOCK_STYLE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_VALUE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_MIN_VALUE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_MAX_VALUE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_STEP)},
+ std::pair{MAP_ENUM(NODE_SLIDER_DIRECTION)},
+ std::pair{MAP_ENUM(NODE_SLIDER_REVERSE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_STYLE)},
+ std::pair{MAP_ENUM(NODE_SLIDER_TRACK_THICKNESS)},
+ std::pair{MAP_ENUM(NODE_RADIO_CHECKED)},
+ std::pair{MAP_ENUM(NODE_RADIO_STYLE)},
+ std::pair{MAP_ENUM(NODE_RADIO_VALUE)},
+ std::pair{MAP_ENUM(NODE_RADIO_GROUP)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_IMAGES)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_STATE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_DURATION)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_REVERSE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_FIXED_SIZE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_FILL_MODE)},
+ std::pair{MAP_ENUM(NODE_IMAGE_ANIMATOR_ITERATION)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_NAME)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_SELECT_ALL)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_SELECTED_COLOR)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_UNSELECTED_COLOR)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_MARK)},
+ std::pair{MAP_ENUM(NODE_CHECKBOX_GROUP_SHAPE)},
+#endif
+ std::pair{MAP_ENUM(NODE_STACK_ALIGN_CONTENT)},
+ std::pair{MAP_ENUM(NODE_SCROLL_BAR_DISPLAY_MODE)},
+ std::pair{MAP_ENUM(NODE_SCROLL_BAR_WIDTH)},
+ std::pair{MAP_ENUM(NODE_SCROLL_BAR_COLOR)},
+ std::pair{MAP_ENUM(NODE_SCROLL_SCROLL_DIRECTION)},
+ std::pair{MAP_ENUM(NODE_SCROLL_EDGE_EFFECT)},
+ std::pair{MAP_ENUM(NODE_SCROLL_ENABLE_SCROLL_INTERACTION)},
+ std::pair{MAP_ENUM(NODE_SCROLL_FRICTION)},
+ std::pair{MAP_ENUM(NODE_SCROLL_SNAP)},
+ std::pair{MAP_ENUM(NODE_SCROLL_NESTED_SCROLL)},
+ std::pair{MAP_ENUM(NODE_SCROLL_OFFSET)},
+ std::pair{MAP_ENUM(NODE_SCROLL_EDGE)},
+ std::pair{MAP_ENUM(NODE_SCROLL_ENABLE_PAGING)},
+ std::pair{MAP_ENUM(NODE_SCROLL_PAGE)},
+ std::pair{MAP_ENUM(NODE_SCROLL_BY)},
+ std::pair{MAP_ENUM(NODE_SCROLL_FLING)},
+ std::pair{MAP_ENUM(NODE_SCROLL_FADING_EDGE)},
+ std::pair{MAP_ENUM(NODE_SCROLL_SIZE)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_SCROLL_CONTENT_START_OFFSET)},
+ std::pair{MAP_ENUM(NODE_SCROLL_CONTENT_END_OFFSET)},
+ std::pair{MAP_ENUM(NODE_SCROLL_BACK_TO_TOP)},
+#endif
+ std::pair{MAP_ENUM(NODE_LIST_DIRECTION)},
+ std::pair{MAP_ENUM(NODE_LIST_STICKY)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_LIST_SPACE)},
+#endif
+ std::pair{MAP_ENUM(NODE_LIST_NODE_ADAPTER)},
+ std::pair{MAP_ENUM(NODE_LIST_CACHED_COUNT)},
+ std::pair{MAP_ENUM(NODE_LIST_SCROLL_TO_INDEX)},
+ std::pair{MAP_ENUM(NODE_LIST_ALIGN_LIST_ITEM)},
+ std::pair{MAP_ENUM(NODE_LIST_CHILDREN_MAIN_SIZE)},
+ std::pair{MAP_ENUM(NODE_LIST_INITIAL_INDEX)},
+ std::pair{MAP_ENUM(NODE_LIST_DIVIDER)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_LIST_SCROLL_TO_INDEX_IN_GROUP)},
+ std::pair{MAP_ENUM(NODE_LIST_LANES)},
+ std::pair{MAP_ENUM(NODE_LIST_SCROLL_SNAP_ALIGN)},
+ std::pair{MAP_ENUM(NODE_LIST_MAINTAIN_VISIBLE_CONTENT_POSITION)},
+#endif
+ std::pair{MAP_ENUM(NODE_SWIPER_LOOP)},
+ std::pair{MAP_ENUM(NODE_SWIPER_AUTO_PLAY)},
+ std::pair{MAP_ENUM(NODE_SWIPER_SHOW_INDICATOR)},
+ std::pair{MAP_ENUM(NODE_SWIPER_INTERVAL)},
+ std::pair{MAP_ENUM(NODE_SWIPER_VERTICAL)},
+ std::pair{MAP_ENUM(NODE_SWIPER_DURATION)},
+ std::pair{MAP_ENUM(NODE_SWIPER_CURVE)},
+ std::pair{MAP_ENUM(NODE_SWIPER_ITEM_SPACE)},
+ std::pair{MAP_ENUM(NODE_SWIPER_INDEX)},
+ std::pair{MAP_ENUM(NODE_SWIPER_DISPLAY_COUNT)},
+ std::pair{MAP_ENUM(NODE_SWIPER_DISABLE_SWIPE)},
+ std::pair{MAP_ENUM(NODE_SWIPER_SHOW_DISPLAY_ARROW)},
+ std::pair{MAP_ENUM(NODE_SWIPER_EDGE_EFFECT_MODE)},
+ std::pair{MAP_ENUM(NODE_SWIPER_NODE_ADAPTER)},
+ std::pair{MAP_ENUM(NODE_SWIPER_CACHED_COUNT)},
+ std::pair{MAP_ENUM(NODE_SWIPER_PREV_MARGIN)},
+ std::pair{MAP_ENUM(NODE_SWIPER_NEXT_MARGIN)},
+ std::pair{MAP_ENUM(NODE_SWIPER_INDICATOR)},
+ std::pair{MAP_ENUM(NODE_SWIPER_NESTED_SCROLL)},
+ std::pair{MAP_ENUM(NODE_SWIPER_SWIPE_TO_INDEX)},
+ std::pair{MAP_ENUM(NODE_SWIPER_INDICATOR_INTERACTIVE)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_SWIPER_PAGE_FLIP_MODE)},
+#endif
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_SWIPE_ACTION)},
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_GROUP_SET_HEADER)},
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_GROUP_SET_FOOTER)},
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_GROUP_SET_DIVIDER)},
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_GROUP_CHILDREN_MAIN_SIZE)},
+#if OHOS_SDK_VERSION > 14
+ std::pair{MAP_ENUM(NODE_LIST_ITEM_GROUP_NODE_ADAPTER)},
+#endif
+ std::pair{MAP_ENUM(NODE_COLUMN_ALIGN_ITEMS)},
+ std::pair{MAP_ENUM(NODE_COLUMN_JUSTIFY_CONTENT)},
+ std::pair{MAP_ENUM(NODE_ROW_ALIGN_ITEMS)},
+ std::pair{MAP_ENUM(NODE_ROW_JUSTIFY_CONTENT)},
+ std::pair{MAP_ENUM(NODE_FLEX_OPTION)},
+ std::pair{MAP_ENUM(NODE_REFRESH_REFRESHING)},
+ std::pair{MAP_ENUM(NODE_REFRESH_CONTENT)},
+ std::pair{MAP_ENUM(NODE_REFRESH_PULL_DOWN_RATIO)},
+ std::pair{MAP_ENUM(NODE_REFRESH_OFFSET)},
+ std::pair{MAP_ENUM(NODE_REFRESH_PULL_TO_REFRESH)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_LAYOUT_DIRECTION)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_COLUMN_TEMPLATE)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_ROW_TEMPLATE)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_COLUMN_GAP)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_ROW_GAP)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_SECTION_OPTION)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_NODE_ADAPTER)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_CACHED_COUNT)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_FOOTER)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_SCROLL_TO_INDEX)},
+ std::pair{MAP_ENUM(NODE_WATER_FLOW_ITEM_CONSTRAINT_SIZE)},
+ std::pair{MAP_ENUM(NODE_RELATIVE_CONTAINER_GUIDE_LINE)},
+ std::pair{MAP_ENUM(NODE_RELATIVE_CONTAINER_BARRIER)},
+ std::pair{MAP_ENUM(NODE_GRID_COLUMN_TEMPLATE)},
+ std::pair{MAP_ENUM(NODE_GRID_ROW_TEMPLATE)},
+ std::pair{MAP_ENUM(NODE_GRID_COLUMN_GAP)},
+ std::pair{MAP_ENUM(NODE_GRID_ROW_GAP)},
+ std::pair{MAP_ENUM(NODE_GRID_NODE_ADAPTER)},
+ std::pair{MAP_ENUM(NODE_GRID_CACHED_COUNT)}
+};
+
+std::string_view nodeAttributeTypeToString(ArkUI_NodeAttributeType type) {
+ auto it = std::find_if(NodeAttributeTypeMap.begin(), NodeAttributeTypeMap.end(),
+ [type](const auto& pair) { return pair.first == type; });
+ return it != NodeAttributeTypeMap.end() ? it->second : "Unknown";
+}
+
+}
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,31 @@
+#ifndef QOHARKUI_H
+#define QOHARKUI_H
+
+#include <QtCore/qglobal.h>
+#include <arkui/native_type.h>
+#include <arkui/native_node.h>
+#include <string>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtOhArkUI {
+
+namespace ErrorCode {
+
+QString toString(int32_t code);
+
+void printErrorCode(const QString &message, int32_t code);
+
+inline bool checkCode(int32_t code)
+{
+ return code == ARKUI_ERROR_CODE_NO_ERROR;
+}
+
+};
+
+std::string_view nodeAttributeTypeToString(ArkUI_NodeAttributeType type);
+
+};
+QT_END_NAMESPACE
+
+#endif // QOHARKUI_H
new file mode 100644
@@ -0,0 +1,937 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 Ltd.
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohauxiliary.cpp
+ * 简要描述: 封装了鸿蒙接口调用的辅助函数集
+ * 创建日期: 2025/01/15
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期: 2025年3月12日
+ * 作者: WangHao
+ * 说明: 添加了Udmf数据读取相关处理
+ ******************************************************************/
+#include <QDebug>
+#include <QTimer>
+#include <QImage>
+#include <QMimeData>
+#include <QEventLoop>
+#include <QTextDocument>
+#include <QLoggingCategory>
+
+#include <set>
+#include <span>
+#include <vector>
+#include <typeinfo>
+#include <functional>
+#include <string_view>
+#include <database/udmf/uds.h>
+#include <database/udmf/udmf.h>
+#include <database/udmf/udmf_meta.h>
+#include <database/udmf/udmf_err_code.h>
+#include <filemanagement/file_uri/oh_file_uri.h>
+
+#include "qohauxiliary.h"
+#include "qohplatformscreen.h"
+#include "qohobjectholder.h"
+
+Q_LOGGING_CATEGORY(ohauxiliary, "qt.ohos.auxiliary")
+
+namespace
+{
+static inline QString ohosPixelMap() { return QStringLiteral("pixelMap"); }
+static inline QString textHtmlLiteral() { return QStringLiteral("text/html"); }
+static inline QString textPlainLiteral() { return QStringLiteral("text/plain"); }
+static inline QString textUriListLiteral() { return QStringLiteral("text/uri-list"); }
+static inline QString applicationXColorLiteral() { return QStringLiteral("application/x-color"); }
+static inline QString applicationXQtImageLiteral() { return QStringLiteral("application/x-qt-image"); }
+
+template<typename T, typename Deleter>
+std::unique_ptr<T, Deleter> newUniqueOrFail(T *ptr, Deleter &&deleter)
+{
+ if (ptr == nullptr) {
+ qFatal("%s: got null pointer with deleter of type '%s'",
+ Q_FUNC_INFO, typeid(deleter).name());
+ }
+
+ return std::unique_ptr<T, Deleter>(ptr, deleter);
+}
+
+static QImage::Format pixelMapFormatToQImage(int32_t format)
+{
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888:
+ return QImage::Format_RGBA8888;
+ case PIXEL_FORMAT_RGB_888:
+ return QImage::Format_RGB888;
+ case PIXEL_FORMAT_ALPHA_8:
+ return QImage::Format_Alpha8;
+ default:
+ return QImage::Format_RGBA8888;
+ }
+}
+
+/*!
+ * \brief 根据像素格式计算缓冲区大小
+ * \param format 像素格式
+ * \param width 图片宽度
+ * \param height 图片高度
+ * \return 返回计算的结果
+ */
+static int bufferSizeHelper(int32_t format, uint32_t width, uint32_t height)
+{
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_F16:
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBA_1010102:
+ break;
+ case PIXEL_FORMAT_NV21:
+ case PIXEL_FORMAT_NV12:
+ return (width * height+((width+1)/2) * ((height+1)/2) * 2);
+ default:
+ break;
+ }
+ return (width * height * 4);
+}
+
+/* FIXME 能否优化? */
+static QImage createQImageFromNativePixelMap(QSharedPointer<::OH_UdsPixelMap> pixelMap)
+{
+ if (pixelMap.isNull())
+ return QImage();
+
+ OH_Pixelmap_InitializationOptions *createOpts;
+ int code = OH_PixelmapInitializationOptions_Create(&createOpts);
+ QOhObjectHolder<OH_Pixelmap_InitializationOptions> optsHolder(createOpts, OH_PixelmapInitializationOptions_Release);
+ Q_UNUSED(optsHolder);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "create pixelmap options failed, code:" << code;
+ return QImage();
+ }
+
+ OH_PixelmapInitializationOptions_SetWidth(createOpts, 6);
+ OH_PixelmapInitializationOptions_SetHeight(createOpts, 4);
+ OH_PixelmapInitializationOptions_SetPixelFormat(createOpts, PIXEL_FORMAT_RGBA_8888);
+ OH_PixelmapInitializationOptions_SetAlphaType(createOpts, PIXELMAP_ALPHA_TYPE_OPAQUE);
+
+ /* NOTE 随意设置的值, 因为OH_PixelmapNative 不能为空 */
+ OH_PixelmapNative *pixelNative;
+ code = OH_PixelmapNative_CreateEmptyPixelmap(createOpts, &pixelNative);
+ QOhObjectHolder<OH_PixelmapNative> pixelHolder(pixelNative, OH_PixelmapNative_Release);
+ Q_UNUSED(pixelHolder);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "create pixelmap native implement failed, code: " << code;
+ return QImage();
+ }
+
+ OH_UdsPixelMap_GetPixelMap(pixelMap.get(), pixelNative);
+ if (Q_NULLPTR == pixelNative) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "pixel native ptr is null.";
+ return QImage();
+ }
+ return QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelNative);
+
+}
+
+template <typename T>
+struct QOhUdsMeta {
+ inline static constexpr std::string_view udmfMetaId { "unknown" };
+};
+
+template <>
+inline constexpr std::string_view QOhUdsMeta<::OH_UdsHtml>::udmfMetaId { UDMF_META_HTML };
+
+template <>
+inline constexpr std::string_view QOhUdsMeta<::OH_UdsPlainText>::udmfMetaId { UDMF_META_PLAIN_TEXT };
+
+template <>
+inline constexpr std::string_view QOhUdsMeta<::OH_UdsFileUri>::udmfMetaId { UDMF_META_GENERAL_FILE_URI };
+
+template <>
+inline constexpr std::string_view QOhUdsMeta<::OH_UdsPixelMap>::udmfMetaId { UDMF_META_OPENHARMONY_PIXEL_MAP };
+
+template <typename T>
+class QOhUdsObject
+{
+ OH_UdmfRecord *const m_record { nullptr };
+ std::variant<std::string, QImage> m_var;
+public:
+ class Wrapper
+ {
+ OH_UdmfRecord *const m_record;
+ public:
+ explicit Wrapper(OH_UdmfRecord *const rd) : m_record(rd) { }
+
+ operator QByteArray() const {
+ if constexpr(std::is_same_v<T, ::OH_UdsHtml>) {
+ QSharedPointer<T> html(OH_UdsHtml_Create(), ::OH_UdsHtml_Destroy);
+
+ int code = OH_UdmfRecord_GetHtml(m_record, html.get());
+ if (::Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "acquire html content failed, error code:" << code;
+ return QByteArray();
+ }
+
+ const char* content = OH_UdsHtml_GetContent(html.get());
+ QString contentStr(content);
+ if(contentStr.isEmpty()) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get html content failed." << content;
+ content = OH_UdsHtml_GetPlainContent(html.get());
+ QString contentStr2(content);
+ if (contentStr2.isEmpty()) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get html plain content failed." << content;
+ return QByteArray();
+ }
+ }
+ qCWarning(ohauxiliary) << "read html data, lenght:" << strlen(content);
+ return QByteArray(content);
+
+ } else if constexpr(std::is_same_v<T, ::OH_UdsPlainText>) {
+ QSharedPointer<T> plainText(OH_UdsPlainText_Create(), ::OH_UdsPlainText_Destroy);
+
+ int code = OH_UdmfRecord_GetPlainText(m_record, plainText.get());
+ if (::Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "acquire plain text failed, error code:" << code;
+ return QByteArray();
+ }
+
+ const char* content = OH_UdsPlainText_GetContent(plainText.get());
+ return QByteArray(content);
+ } else if constexpr(std::is_same_v<T, ::OH_UdsFileUri>) {
+ QSharedPointer<T> fileUri(OH_UdsFileUri_Create(), ::OH_UdsFileUri_Destroy);
+ int code = OH_UdmfRecord_GetFileUri(m_record, fileUri.get());
+ if (::Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get file uri failed, error code:" << code;
+ return QByteArray();
+ }
+
+ QByteArray url;
+ const char *content = OH_UdsFileUri_GetFileUri(fileUri.get());
+ if (Q_NULLPTR != content) {
+ char *data = nullptr;
+ FileManagement_ErrCode errCode = OH_FileUri_GetPathFromUri(content, strlen(content), &data);
+ if (ERR_OK == errCode && nullptr != data) {
+ url = QByteArray(data);
+ free(data);
+ }
+ }
+ return url;
+ } else {
+ return QByteArray();
+ }
+
+ return QByteArray();
+ };
+
+ QSharedPointer<T> get() const {
+ if constexpr(std::is_same_v<T, ::OH_UdsPixelMap>) {
+ QSharedPointer<T> pixelMap(OH_UdsPixelMap_Create(), ::OH_UdsPixelMap_Destroy);
+
+ int code = OH_UdmfRecord_GetPixelMap(m_record, pixelMap.get());
+ if (::Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "create pixel map failed, error code:" << code;
+ return QSharedPointer<T>(nullptr);
+ }
+
+ return pixelMap;
+ } else {
+ return QSharedPointer<T>(nullptr);
+ }
+ }
+ };
+
+ explicit QOhUdsObject(const QImage &image) : m_var(image) {}
+ QOhUdsObject(std::string str) : m_var(str) {}
+ QOhUdsObject(::OH_UdmfRecord *const record) : m_record(record){}
+
+ Wrapper getContent() const {
+ return Wrapper(m_record);
+ }
+
+ T* data() {
+ if constexpr(std::is_same_v<T, ::OH_UdsHtml>) {
+ std::unique_ptr<::OH_UdsHtml, void(*)(OH_UdsHtml*)>
+ htmlUds(newUniqueOrFail(::OH_UdsHtml_Create(), ::OH_UdsHtml_Destroy));
+
+ int code = ::OH_UdsHtml_SetContent(htmlUds.get(), std::get<std::string>(m_var).c_str());
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdsHtml_SetContent failed, error code:" << code;
+ return nullptr;
+ }
+ return htmlUds.release();
+ } else if constexpr(std::is_same_v<T, ::OH_UdsPlainText>) {
+ std::unique_ptr<::OH_UdsPlainText, void(*)(OH_UdsPlainText*)>
+ textUds(newUniqueOrFail(::OH_UdsPlainText_Create(), ::OH_UdsPlainText_Destroy));
+
+ int code = OH_UdsPlainText_SetContent(textUds.get(), std::get<std::string>(m_var).c_str());
+ if (UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdsPlainText_SetContent failed, error code:" << code;
+ return nullptr;
+ }
+ return textUds.release();
+ } else if constexpr(std::is_same_v<T, ::OH_UdsFileUri>) {
+ std::unique_ptr<::OH_UdsFileUri, void(*)(::OH_UdsFileUri*)>
+ fileUri(newUniqueOrFail(::OH_UdsFileUri_Create(), ::OH_UdsFileUri_Destroy));
+ int code = ::OH_UdsFileUri_SetFileType(fileUri.get(), UDMF_META_GENERAL_FILE);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary, "%s set file type failed, code: %d", Q_FUNC_INFO, code);
+ }
+
+ code = ::OH_UdsFileUri_SetFileUri(fileUri.get(), std::get<std::string>(m_var).c_str());
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary, "%s set file uri failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+ return fileUri.release();
+ } else if constexpr(std::is_same_v<T, ::OH_UdsPixelMap>) {
+ std::unique_ptr<::OH_UdsPixelMap, void(*)(OH_UdsPixelMap*)>
+ pixelMapUds(newUniqueOrFail(::OH_UdsPixelMap_Create(), ::OH_UdsPixelMap_Destroy));
+
+
+ int code = OH_UdsPixelMap_SetPixelMap(pixelMapUds.get(), QtOh::UdmfHelper::createNativePixelMapFromQImage(std::get<QImage>(m_var)));
+ if (::Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary, "%s set pixelmap data failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ return pixelMapUds.release();
+ } else {
+ return nullptr;
+ }
+
+ }
+};
+
+class QOhUdmfRecord
+{
+ friend class QOhUdmfData;
+ Q_DISABLE_COPY(QOhUdmfRecord)
+
+ std::unique_ptr<::OH_UdmfRecord, void(*)(::OH_UdmfRecord *)> m_primitive;
+
+public:
+ QOhUdmfRecord();
+ QOhUdmfRecord(QOhUdmfRecord &&) = default;
+ QOhUdmfRecord &operator=(QOhUdmfRecord &&) = default;
+
+ std::vector<std::string> getTypes();
+
+ template <typename T>
+ void addUdsObj(QOhUdsObject<T> udsObj) {
+ if constexpr(std::is_same_v<T, ::OH_UdsHtml>) {
+ T *uds = udsObj.data();
+ if (nullptr != uds) {
+ int code = OH_UdmfRecord_AddHtml(m_primitive.get(), uds);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdmfRecord_AddHtml failed, error code:" << code;
+ }
+ }
+ } else if constexpr(std::is_same_v<T, ::OH_UdsPlainText>) {
+ T *uds = udsObj.data();
+ if (nullptr != uds) {
+ int code = OH_UdmfRecord_AddPlainText(m_primitive.get(), uds);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdmfRecord_AddPlainText failed, error code:" << code;
+ }
+ }
+ } else if constexpr(std::is_same_v<T, ::OH_UdsFileUri>) {
+ T *uds = udsObj.data();
+ if (nullptr != uds) {
+ int code = OH_UdmfRecord_AddFileUri(m_primitive.get(), uds);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdmfRecord_AddFileUri failed, error code:" << code;
+ }
+ }
+ } else if constexpr(std::is_same_v<T, ::OH_UdsPixelMap>) {
+ T *uds = udsObj.data();
+ if (nullptr != uds) {
+ int code = OH_UdmfRecord_AddPixelMap(m_primitive.get(), uds);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "OH_UdmfRecord_AddPixelMap failed, error code:" << code;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ QOhUdsObject<T> getUdsObj() {
+ return QOhUdsObject<T>(m_primitive.get());
+ }
+
+ std::optional<QByteArray> tryGetGeneralEntry(std::string_view typeId);
+ void addGeneralEntry(const std::string_view &typeId, uchar *buff, uint size);
+
+private:
+ ::OH_UdmfRecord *release();
+ static QOhUdmfRecord makeAsView(::OH_UdmfRecord *primitive);
+ QOhUdmfRecord(std::unique_ptr<::OH_UdmfRecord, void(*)(::OH_UdmfRecord *)> &&primitive);
+};
+
+QOhUdmfRecord::QOhUdmfRecord()
+ : QOhUdmfRecord(newUniqueOrFail(OH_UdmfRecord_Create(), OH_UdmfRecord_Destroy))
+{
+
+}
+
+std::vector<std::string> QOhUdmfRecord::getTypes()
+{
+ quint32 count(0);
+ std::vector<std::string> typelist{};
+ char **list = OH_UdmfRecord_GetTypes(m_primitive.get(), &count);
+ if (nullptr == list)
+ return typelist;
+
+ std::span<char *> types(list, count);
+ for (auto type : types)
+ typelist.emplace_back(type);
+
+ //delete [] list; //FIXME
+ return typelist;
+}
+
+std::optional<QByteArray> QOhUdmfRecord::tryGetGeneralEntry(std::string_view typeId)
+{
+ auto existsTypes = getTypes();
+ auto it = std::find(std::begin(existsTypes), std::end(existsTypes), typeId);
+ if (it == std::end(existsTypes))
+ return std::nullopt;
+
+ unsigned int count { 0 };
+ unsigned char *entry { nullptr };
+
+ int code = OH_UdmfRecord_GetGeneralEntry(m_primitive.get(), typeId.data(), &entry, &count);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary,
+ "%s OH_UdmfRecord_GetGeneralEntry result error, type: %s erro code: %d",
+ Q_FUNC_INFO, typeId.data(), code);
+
+ return std::nullopt;
+ }
+
+ return QByteArray(reinterpret_cast<char*>(entry), count);
+}
+
+void QOhUdmfRecord::addGeneralEntry(const std::string_view &typeId, uchar *buff, uint size)
+{
+ int code = OH_UdmfRecord_AddGeneralEntry(m_primitive.get(), typeId.data(), buff, size);
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary,
+ "%s OH_UdmfRecord_AddGeneralEntry result error, type: %s erro code: %d",
+ Q_FUNC_INFO, typeId.data(), code);
+ }
+}
+
+QOhUdmfRecord QOhUdmfRecord::makeAsView(OH_UdmfRecord *primitive)
+{
+ return QOhUdmfRecord(
+ std::unique_ptr<::OH_UdmfRecord, void (*)(::OH_UdmfRecord *)>(
+ primitive,
+ [](::OH_UdmfRecord *) {
+ }));
+}
+
+QOhUdmfRecord::QOhUdmfRecord(std::unique_ptr<OH_UdmfRecord, void (*)(::OH_UdmfRecord *)> &&primitive)
+ : m_primitive(std::move(primitive))
+{
+
+}
+
+OH_UdmfRecord *QOhUdmfRecord::release()
+{
+ return m_primitive.release();
+}
+
+class QOhUdmfData
+{
+ Q_DISABLE_COPY(QOhUdmfData)
+ std::unique_ptr<::OH_UdmfData, void(*)(::OH_UdmfData *)> m_primitive;
+
+public:
+ static QOhUdmfData takeOwnership(::OH_UdmfData *nativePtr);
+
+ QOhUdmfData();
+ QOhUdmfData(QOhUdmfData &&) = default;
+ QOhUdmfData &operator=(QOhUdmfData &&) = default;
+
+ std::vector<std::string> getTypes() const;
+
+ void addRecord(QOhUdmfRecord &&record);
+ std::vector<QOhUdmfRecord> getRecords();
+
+ ::OH_UdmfData *release();
+ ::OH_UdmfData *primitive();
+
+private:
+ QOhUdmfData(::OH_UdmfData *primitive);
+};
+
+QOhUdmfData QOhUdmfData::takeOwnership(OH_UdmfData *primitive)
+{
+ return QOhUdmfData(primitive);
+}
+
+QOhUdmfData::QOhUdmfData()
+ : QOhUdmfData(::OH_UdmfData_Create())
+{
+
+}
+
+std::vector<std::string> QOhUdmfData::getTypes() const
+{
+ char **types = nullptr;
+ unsigned int count = 0;
+ types = OH_UdmfData_GetTypes(m_primitive.get(), &count);
+ if (nullptr != types)
+ return {types, types + count};
+
+ return {};
+}
+
+void QOhUdmfData::addRecord(QOhUdmfRecord &&record)
+{
+ int code = ::OH_UdmfData_AddRecord(m_primitive.get(), std::move(record).release());
+ if (Udmf_ErrCode::UDMF_E_OK != code) {
+ qCWarning(ohauxiliary) << "add ohdmf record failed, code:" << code;
+ }
+}
+
+std::vector<QOhUdmfRecord> QOhUdmfData::getRecords()
+{
+ quint32 rdLength(0);
+ OH_UdmfRecord** rds = OH_UdmfData_GetRecords(m_primitive.get(), &rdLength);
+ // qCInfo(ohauxiliary) << Q_FUNC_INFO << "get data count:" << rdLength;
+
+ std::vector<QOhUdmfRecord> records;
+ for (quint32 i = 0; i < rdLength; ++i)
+ records.emplace_back(QOhUdmfRecord::makeAsView(rds[i]));
+
+ return records;
+}
+
+QOhUdmfData::QOhUdmfData(OH_UdmfData *primitive)
+ :m_primitive(newUniqueOrFail(primitive, ::OH_UdmfData_Destroy))
+{
+
+}
+
+OH_UdmfData *QOhUdmfData::primitive()
+{
+ return m_primitive.get();
+}
+
+OH_UdmfData *QOhUdmfData::release()
+{
+ return m_primitive.release();
+}
+
+template <typename T>
+using QOhConsumer = std::function<void(const T&)>;
+
+template <typename T>
+void processRecordsByType(std::span<QOhUdmfRecord> records,
+ const QOhConsumer<QOhUdsObject<T>> &func)
+{
+ for (auto &record : records) {
+ for (auto &typeId : record.getTypes()) {
+ if (QOhUdsMeta<T>::udmfMetaId == std::string_view(typeId))
+ func(record.getUdsObj<T>());
+ }
+ }
+}
+
+bool hasQMimeDataPeerType(std::string_view type)
+{
+ static std::set<std::string_view> types {
+ QOhUdsMeta<::OH_UdsHtml>::udmfMetaId,
+ QOhUdsMeta<::OH_UdsFileUri>::udmfMetaId,
+ QOhUdsMeta<::OH_UdsPixelMap>::udmfMetaId,
+ QOhUdsMeta<::OH_UdsPlainText>::udmfMetaId
+ };
+
+ return types.find(type) != types.end();
+}
+
+bool isUdmfMetaFileType(std::string_view type)
+{
+ static std::set<std::string_view> types {
+ UDMF_META_GENERAL_FILE,
+ UDMF_META_AUDIO,
+ UDMF_META_FOLDER,
+ UDMF_META_IMAGE,
+ UDMF_META_VIDEO,
+ };
+
+ return types.find(type) != types.end();
+}
+
+void extractFileUrisToMimeData(std::span<QOhUdmfRecord> records, QMimeData * const mimeData)
+{
+ QList<QUrl> urls;
+ processRecordsByType<::OH_UdsFileUri>(records, [&urls](auto udsObj) {
+ urls.append(QUrl::fromLocalFile(QString::fromLocal8Bit(udsObj.getContent())));
+ });
+
+ if (!urls.isEmpty())
+ mimeData->setUrls(urls);
+}
+
+void extractGeneralEntriesToMimeData(std::span<QOhUdmfRecord> records, QMimeData * const mimeData)
+{
+ for (auto &record : records) {
+ for (auto typeId : record.getTypes()) {
+ if (!hasQMimeDataPeerType(typeId) && !isUdmfMetaFileType(typeId)) {
+ auto data = record.tryGetGeneralEntry(typeId);
+ if (!data.has_value())
+ continue;
+
+ mimeData->setData(QString::fromStdString(typeId),
+ data.value());
+ }
+ }
+ }
+}
+
+}
+
+namespace QtOh {
+
+QImage UdmfHelper::createQImageFromPixelmapNative(OH_PixelmapNative *pixelNative)
+{
+ OH_Pixelmap_ImageInfo *imageInfo(Q_NULLPTR);
+ int code = OH_PixelmapImageInfo_Create(&imageInfo);
+ QOhObjectHolder<OH_Pixelmap_ImageInfo> imageInfoHolder(imageInfo, OH_PixelmapImageInfo_Release);
+ Q_UNUSED(imageInfoHolder);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "create pixel image info, error code:" << code;
+ return QImage();
+ }
+
+ code = OH_PixelmapNative_GetImageInfo(pixelNative, imageInfo);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info failed, error code:" << code;
+ return QImage();
+ }
+
+ /* 获取图片的宽,高,pixel格式,透明度等信息 */
+ uint32_t width, height, rowStride;
+ int32_t pixelFormat, alphaType;
+
+ code = OH_PixelmapImageInfo_GetWidth(imageInfo, &width);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info width failed, code:" << code;
+ return QImage();
+ }
+ code = OH_PixelmapImageInfo_GetHeight(imageInfo, &height);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info height failed, code:" << code;
+ return QImage();
+ }
+
+ code = OH_PixelmapImageInfo_GetRowStride(imageInfo, &rowStride);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info rowStride failed, code:" << code;
+ return QImage();
+ }
+ code = OH_PixelmapImageInfo_GetAlphaType(imageInfo, &alphaType);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info alphaType failed, code:" << code;
+ return QImage();
+ }
+
+ code = OH_PixelmapImageInfo_GetPixelFormat(imageInfo, &pixelFormat);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "get image info pixelFormat failed, code:" << code;
+ return QImage();
+ }
+
+ auto dest = new QVector<uint8_t>(bufferSizeHelper(pixelFormat, width, height));
+
+ size_t size = dest->size();
+ code = OH_PixelmapNative_ReadPixels(pixelNative, dest->data(), &size);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary) << Q_FUNC_INFO << "read pixels failed, error code:" << code;
+ return QImage();
+ }
+
+ QImage image(dest->data(), int(width), int(height),
+ pixelMapFormatToQImage(pixelFormat), [](void *info){
+ delete reinterpret_cast<QVector<uint8_t>*>(info);
+ }, reinterpret_cast<void*>(dest));
+
+ if(PIXEL_FORMAT_BGRA_8888 == pixelFormat){
+ image = image.rgbSwapped();
+ }
+
+ return image;
+}
+
+/*!
+ * \brief 将QImage转为鸿蒙像素图数据
+ * \param image QImage参数
+ * \return 返回OH_PixelmapNative指针
+ * 调用者负责使用OH_PixelmapNative_Release清理数据
+ */
+OH_PixelmapNative *UdmfHelper::createNativePixelMapFromQImage(const QImage &image)
+{
+ if (image.isNull())
+ return nullptr;
+ OH_Pixelmap_InitializationOptions *createOptionNative(nullptr);
+ int code = OH_PixelmapInitializationOptions_Create(&createOptionNative);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s create pixelmap options failed, code: %d", Q_FUNC_INFO, code);
+ }
+
+ std::unique_ptr<OH_Pixelmap_InitializationOptions,
+ Image_ErrorCode(*)(OH_Pixelmap_InitializationOptions*)>
+ createOpts(newUniqueOrFail(createOptionNative, ::OH_PixelmapInitializationOptions_Release));
+
+ auto qImageFormatToPixelMap = [](QImage::Format format, bool &premultiplied)->int32_t{
+ switch (format) {
+ case QImage::Format_Alpha8:
+ return (premultiplied = false, PIXEL_FORMAT_ALPHA_8);
+ case QImage::Format_RGB888:
+ return (premultiplied = false, PIXEL_FORMAT_RGB_888);
+ case QImage::Format_RGBA8888:
+ return (premultiplied = false, PIXEL_FORMAT_RGBA_8888);
+ case QImage::Format_RGBA8888_Premultiplied:
+ return (premultiplied = true, PIXEL_FORMAT_RGBA_8888);
+ default:
+ break;
+ }
+ return PIXEL_FORMAT_UNKNOWN;
+ };
+
+ auto alphaTypeFunc = [](const QImage &image, bool premultiplied)->int32_t {
+ if (!image.hasAlphaChannel()) {
+ return PIXELMAP_ALPHA_TYPE_OPAQUE;
+ } else if (image.hasAlphaChannel() && premultiplied) {
+ return PIXELMAP_ALPHA_TYPE_PREMULTIPLIED;
+ } else {
+ return PIXELMAP_ALPHA_TYPE_UNKNOWN;
+ }
+ };
+
+ bool ispre = false;
+ int32_t format = PIXEL_FORMAT_RGBA_8888;
+ format = qImageFormatToPixelMap(image.format(), ispre);
+ int32_t alphaType = alphaTypeFunc(image, ispre);
+
+ /* FIXME 鸿蒙不支持的格式需要转换
+ * 当前统一转换为QImage::Format_RGBA8888
+ */
+ QImage ohImage = image;
+ if (PIXEL_FORMAT_UNKNOWN == format)
+ ohImage = ohImage.convertToFormat(QImage::Format_RGBA8888);
+
+ code = OH_PixelmapInitializationOptions_SetWidth(createOpts.get(), ohImage.width());
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s set pixelmap width failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ code = OH_PixelmapInitializationOptions_SetHeight(createOpts.get(), ohImage.height());
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s set pixelmap height failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ code = OH_PixelmapInitializationOptions_SetPixelFormat(createOpts.get(), format);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s set pixelmap format failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ code = OH_PixelmapInitializationOptions_SetAlphaType(createOpts.get(), alphaType);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s set pixelmap alpha type failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ /* 创建Pixelmap实例 */
+ OH_PixelmapNative *pixelNative(nullptr);
+ code = OH_PixelmapNative_CreateEmptyPixelmap(createOpts.get(), &pixelNative);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s create pixelmap native implement failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ std::unique_ptr<OH_PixelmapNative, Image_ErrorCode(*)(OH_PixelmapNative*)> pixelMapNative
+ (newUniqueOrFail(pixelNative, ::OH_PixelmapNative_Release));
+
+
+ qsizetype size = ohImage.sizeInBytes();
+ code = OH_PixelmapNative_WritePixels(pixelMapNative.get(), const_cast<uchar*>(ohImage.bits()), size);
+ if (Image_ErrorCode::IMAGE_SUCCESS != code) {
+ qCWarning(ohauxiliary, "%s OH_PixelmapNative_WritePixels result failed, code: %d", Q_FUNC_INFO, code);
+ return nullptr;
+ }
+
+ return pixelMapNative.release();
+}
+
+/*!
+ * \brief 读取OH_UdmfData中的数据到QMimeData.
+ * 传入的OH_UdmfData参数,函数内部使用智能指针管理并负责清理数据.
+ * \param udmfData OH_UdmfData数据
+ * \param mimeData QMimeData指针
+ */
+void UdmfHelper::acquireDatasFromUdmfToMime(::OH_UdmfData *const udmfData, QMimeData * const mimeData)
+{
+ if (!udmfData || !mimeData)
+ return;
+
+ QOhUdmfData ohUdmfData = QOhUdmfData::takeOwnership(udmfData);
+ auto records = ohUdmfData.getRecords();
+ if (records.empty())
+ return;
+
+ /* html */
+ processRecordsByType<::OH_UdsHtml>(std::span<QOhUdmfRecord>(records.data(), 1), [mimeData](auto udsObj){
+ mimeData->setHtml(QString::fromUtf8(udsObj.getContent()));
+ });
+
+ /* 文本 */
+ processRecordsByType<::OH_UdsPlainText>(std::span<QOhUdmfRecord>(records.data(), 1), [mimeData](auto udsObj){
+ mimeData->setText(QString::fromUtf8(udsObj.getContent()));
+ });
+
+ /* PixelMap */
+ processRecordsByType<::OH_UdsPixelMap>(std::span<QOhUdmfRecord>(records.data(), 1), [mimeData](auto udsObj){
+ mimeData->setImageData(createQImageFromNativePixelMap(udsObj.getContent().get()));
+ });
+
+ /* fileuri */
+ extractFileUrisToMimeData(std::span<QOhUdmfRecord>(records.data(), records.size()), mimeData);
+
+ /* 自定义 */
+ extractGeneralEntriesToMimeData(std::span<QOhUdmfRecord>(records.data(), 1), mimeData);
+}
+
+/*!
+ * \brief 返回的OH_UdmfData,在向系统接口添加该OH_UdmfData失败时
+ * 需要调用方负责OH_UdmfData_Destroy
+ */
+::OH_UdmfData *UdmfHelper::makeUdmfDataFromMime(QMimeData * const mimeData, std::optional<bool> shareOnlyApp)
+{
+ std::vector<std::function<void(QOhUdmfRecord &)>> addUdsFuncs;
+
+ for (auto format : mimeData->formats()) {
+ if (format == textHtmlLiteral()) {
+ const QString &html = mimeData->html();
+ addUdsFuncs.emplace_back(
+ [html](QOhUdmfRecord &record) {
+ record.addUdsObj(QOhUdsObject<::OH_UdsHtml>(html.toStdString()));
+ });
+ } else if (format == textPlainLiteral()) {
+ const QString text = mimeData->text();
+ addUdsFuncs.emplace_back(
+ [text](QOhUdmfRecord &record) {
+ record.addUdsObj(QOhUdsObject<::OH_UdsPlainText>(text.toStdString()));
+ });
+ } else if (format == textUriListLiteral()) {
+ auto urls = mimeData->urls();
+ auto url = urls.first().toString();
+ addUdsFuncs.emplace_back(
+ [url](QOhUdmfRecord &record) {
+ record.addUdsObj(QOhUdsObject<::OH_UdsFileUri>(url.toStdString()));
+ });
+
+ } else if (format == applicationXQtImageLiteral()) {
+ auto image = qvariant_cast<QImage>(mimeData->imageData());
+ addUdsFuncs.emplace_back(
+ [image](QOhUdmfRecord &record) {
+ record.addUdsObj(QOhUdsObject<::OH_UdsPixelMap>(image));
+ });
+ } else {
+ auto dataBytes = mimeData->data(format);
+ addUdsFuncs.emplace_back(
+ [format, dataBytes](QOhUdmfRecord &record) mutable {
+ record.addGeneralEntry(
+ format.toStdString(),
+ reinterpret_cast<uchar *>(dataBytes.data()), dataBytes.length());
+ });
+ }
+ }
+
+ std::vector<std::function<QOhUdmfRecord()>> records;
+ if (!addUdsFuncs.empty()) {
+ records.emplace_back(
+ [addUdsFuncs = std::move(addUdsFuncs)]() {
+ QOhUdmfRecord record;
+ for (const auto &addUds : addUdsFuncs) {
+ addUds(record);
+ }
+ return record;
+ });
+ }
+
+ auto urls = mimeData->urls();
+ for (int i = 1; i < urls.size(); ++i) {
+ auto url = urls.at(i).toString();
+ records.emplace_back(
+ [url]() {
+ QOhUdmfRecord uriRecord;
+ uriRecord.addUdsObj(QOhUdsObject<::OH_UdsFileUri>(url.toStdString()));
+ return uriRecord;
+ });
+ }
+
+ QOhUdmfData ohUdmfData;
+ if (shareOnlyApp.has_value()) {
+ ::OH_UdmfProperty* property = ::OH_UdmfProperty_Create(ohUdmfData.primitive());
+ if (nullptr == property) {
+ qCWarning(ohauxiliary, "%s create OH_UdmfProperty_Create failed.", Q_FUNC_INFO);
+ return ohUdmfData.release();
+ }
+
+ int ret = OH_UdmfProperty_SetShareOption(property, shareOnlyApp.value() ?
+ ::Udmf_ShareOption::SHARE_OPTIONS_IN_APP :
+ ::Udmf_ShareOption::SHARE_OPTIONS_CROSS_APP);
+
+ if (::Udmf_ErrCode::UDMF_E_OK != ret) {
+ ::OH_UdmfProperty_Destroy(property);
+ return ohUdmfData.release();
+ }
+
+ }
+
+ for (const auto &record : records) {
+ ohUdmfData.addRecord(record());
+ }
+
+ return ohUdmfData.release();
+}
+
+bool wait(QEventLoop *loop, int ms)
+{
+ QTimer timer;
+ timer.setSingleShot(true);
+ QObject::connect(&timer, &QTimer::timeout, loop, std::bind(&QEventLoop::exit, loop, -1));
+ timer.start(ms);
+ return loop->exec() == -1;
+}
+
+bool isEnvironmentVariableIsTrue(const char *env, bool defaultValue)
+{
+ if (!qEnvironmentVariableIsSet(env))
+ return defaultValue;
+ QByteArray value = qgetenv(env);
+ if (value.isEmpty())
+ return false;
+ if (value == "true" || value.toInt() != 0)
+ return true;
+ return false;
+}
+
+qreal densityPixels(QPlatformScreen *screen)
+{
+ if (screen == nullptr)
+ return 1.0;
+ if (QOhPlatformScreen *ps = dynamic_cast<QOhPlatformScreen *>(screen)) {
+ return ps->densityPixels();
+ }
+ return screen->devicePixelRatio();
+}
+
+}
new file mode 100644
@@ -0,0 +1,217 @@
+/*******************************************************************
+ * Copyright(c) 2022-2025 Ltd.
+ * All right reserved. See LGPL for detailed Information
+ *
+ * 文件名称: qohauxiliary.h
+ * 简要描述: 封装了鸿蒙接口调用的辅助函数集
+ * 创建日期: 2025/01/15
+ * 作者: WangHao
+ * 说明:
+ *
+ * 修改日期: 2025年3月12日
+ * 作者: WangHao
+ * 说明: 添加了Udmf数据读取相关处理
+ ******************************************************************/
+#ifndef QOHAUXILIARY_H
+#define QOHAUXILIARY_H
+
+#include <QString>
+#include <optional>
+#include <QByteArray>
+#include <QRect>
+#include <QSharedPointer>
+#include <QJsObject>
+#include <QThread>
+#include <QGuiApplication>
+#include <qpa/qplatformscreen.h>
+#include <qohutility.h>
+#include "qjssettings.h"
+
+class QMimeData;
+class QEventLoop;
+struct OH_UdmfData;
+struct OH_UdmfRecord;
+struct OH_PixelmapNative;
+class QOhPlatformClipboard;
+QT_BEGIN_NAMESPACE
+namespace QtOh {
+
+/* 系统窗口状态 */
+enum class WindowStatusType {
+ UNDEFINED = 0, /* 表示APP未定义窗口模式 */
+ FULL_SCREEN = 1, /* 表示APP全屏模式 */
+ MAXIMIZE = 2, /* 表示APP窗口最大化模式 */
+ MINIMIZE = 3, /* 表示APP窗口最小化模式 */
+ FLOATING = 4, /* 表示APP自由悬浮形式窗口模式 */
+ SPLIT_SCREEN = 5, /* 表示APP分屏模式 */
+ SURFACE_SHOW = 6
+};
+
+/* harmony os state */
+enum class WindowState {
+ WINDOW_SHOWN = 1,
+ WINDOW_ACTIVE = 2,
+ WINDOW_INACTIVE = 3,
+ WINDOW_HIDDEN = 4,
+ WINDOW_DESTROYED = 7
+};
+
+class UdmfHelper
+{
+ friend QOhPlatformClipboard;
+
+public:
+ static QImage createQImageFromPixelmapNative(OH_PixelmapNative *pixelmapNative);
+ static OH_PixelmapNative* createNativePixelMapFromQImage(const QImage &image);
+ static void acquireDatasFromUdmfToMime(::OH_UdmfData *const udmfData, QMimeData * const mimeData);
+ static ::OH_UdmfData * makeUdmfDataFromMime(QMimeData * const mimeData, std::optional<bool> shareOnlyApp = std::nullopt);
+};
+
+inline std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy)
+{
+ class DestroyNotifier
+ {
+ public:
+ explicit DestroyNotifier(std::function<void()> callOnDestroy)
+ : callOnDestroy(std::move(callOnDestroy))
+ {
+ }
+
+ DestroyNotifier(const DestroyNotifier &) = delete;
+ DestroyNotifier(DestroyNotifier &&) = delete;
+ DestroyNotifier &operator=(const DestroyNotifier &) = delete;
+ DestroyNotifier &operator=(DestroyNotifier &&) = delete;
+
+ ~DestroyNotifier()
+ {
+ callOnDestroy();
+ };
+
+ private:
+ std::function<void()> callOnDestroy;
+ };
+
+ return std::make_shared<DestroyNotifier>(std::move(callOnDestroy));
+}
+
+inline QRect jsRect2QRect(const Napi::Object &rect)
+{
+ return QRect(rect.Get("left").ToNumber(), rect.Get("top").ToNumber(),
+ rect.Get("width").ToNumber(), rect.Get("height").ToNumber());
+}
+
+/*!
+ * \brief 等待函数,超时返回true
+ */
+bool wait(QEventLoop *loop, int ms);
+
+template <typename Function, typename... Args>
+void runOnQtMainThread(Function&& function, Args&&... args)
+{
+ if (QCoreApplication::instance() == nullptr)
+ return;
+
+ auto callable = [function, ...args = std::forward<Args>(args)]() mutable {
+ std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
+ };
+
+ if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
+ callable();
+ } else {
+ QMetaObject::invokeMethod(QCoreApplication::instance(), callable, Qt::QueuedConnection);
+ }
+}
+
+template <typename Function, typename... Args>
+auto runOnQtMainThreadWithResult(Function&& function, Args&&... args)
+ -> decltype(std::invoke(std::forward<Function>(function), std::forward<Args>(args)...))
+{
+ using ReturnType = decltype(std::invoke(std::forward<Function>(function), std::forward<Args>(args)...));
+ auto promise = std::make_shared<std::promise<ReturnType>>();
+ auto future = promise->get_future();
+
+ auto callable = [promise, function, ...args = std::forward<Args>(args)]() mutable {
+ if constexpr (std::is_void_v<ReturnType>) {
+ std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
+ promise->set_value();
+ } else {
+ promise->set_value(std::invoke(std::forward<Function>(function), std::forward<Args>(args)...));
+ }
+ };
+
+ if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
+ callable();
+ } else {
+ QMetaObject::invokeMethod(QCoreApplication::instance(), callable, Qt::QueuedConnection);
+ }
+
+ return future.get();
+}
+
+template <typename Function, typename... Args>
+void runOnQtMainThreadAndWait(Function&& function, Args&&... args)
+{
+ auto promise = std::make_shared<std::promise<void>>();
+ auto future = promise->get_future();
+
+ auto callable = [promise, function, ...args = std::forward<Args>(args)]() mutable {
+ std::invoke(std::forward<Function>(function), std::forward<Args>(args)...);
+ promise->set_value();
+ };
+
+ if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
+ callable();
+ } else {
+ QMetaObject::invokeMethod(QCoreApplication::instance(), callable, Qt::QueuedConnection);
+ }
+
+ return future.get();
+}
+
+inline void runOnQtMainThreadLater(const std::function<void ()> &f)
+{
+ if (QCoreApplication::instance() == nullptr || !f)
+ return;
+ QMetaObject::invokeMethod(QCoreApplication::instance(), f, Qt::QueuedConnection);
+}
+
+inline bool isFreeWindowEnable()
+{
+ return QJsSettings::instance()->isFreeWindowEnable();
+}
+
+inline bool isTabletDevice()
+{
+ return (QtOh::Utility::type() == QtOh::Utility::Tablet);
+}
+
+inline bool isPhoneDevice()
+{
+ return (QtOh::Utility::type() == QtOh::Utility::Phone);
+}
+
+inline bool isSupportFreeWindow()
+{
+ return (QtOh::Utility::type() == QtOh::Utility::PC ||
+ ((QtOh::isTabletDevice() || QtOh::isPhoneDevice()) && QtOh::isFreeWindowEnable()));
+}
+
+inline bool checkDeviceType(const QtOh::Utility::DeviceTypes deviceTypes)
+{
+ return QtOh::Utility::type() & deviceTypes;
+}
+
+inline bool isOpenHarmonyDevice()
+{
+ return QtOh::Utility::distributionOSName() == QStringLiteral("OpenHarmony");
+}
+
+qreal densityPixels(QPlatformScreen *screen);
+
+// 如果环境变量没有设置返回defaultValue
+bool isEnvironmentVariableIsTrue(const char *env, bool defaultValue = true);
+
+}
+
+QT_END_NAMESPACE
+#endif // QOHAUXILIARY_H
new file mode 100644
@@ -0,0 +1,20 @@
+#include "qohcompatibility.h"
+#include <qopenharmony.h>
+
+#include <QHash>
+#include <QPair>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+void QOhCompatibility::init()
+{
+ auto v = QtOh::apiVersion();
+ if (v < OHOS_SDK_VERSION) {
+ qWarning() << "OS api version is less than build api version:"
+ << QString("the os version is %1 and build version is %2").arg(v).arg(OHOS_SDK_VERSION);
+ }
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,299 @@
+#ifndef QOHCOMPATIBILITY_H
+#define QOHCOMPATIBILITY_H
+
+#include <QString>
+#include <QDebug>
+#include <QStringList>
+#include <dlfcn.h>
+#if OHOS_SDK_VERSION > 13
+#include <window_manager/oh_display_capture.h>
+#include <arkui/native_key_event.h>
+#else
+typedef enum {
+ /** Unknown type **/
+ ARKUI_KEY_EVENT_UNKNOWN = -1,
+ /** Pressing of a key **/
+ ARKUI_KEY_EVENT_DOWN = 0,
+ /** Release of a key **/
+ ARKUI_KEY_EVENT_UP = 1,
+ /** Long press of a key **/
+ ARKUI_KEY_EVENT_LONG_PRESS = 2,
+ /** Click of a key **/
+ ARKUI_KEY_EVENT_CLICK = 3,
+} ArkUI_KeyEventType;
+#endif
+
+#if OHOS_SDK_VERSION >= 15
+#else
+typedef bool (*OH_NativeWindowManager_MouseEventFilter)(Input_MouseEvent* mouseEvent);
+typedef bool (*OH_NativeWindowManager_TouchEventFilter)(Input_TouchEvent* touchEvent);
+#endif
+
+#include <database/pasteboard/oh_pasteboard.h>
+#include <arkui/native_node.h>
+#include <window_manager/oh_window_comm.h>
+#include <window_manager/oh_window_event_filter.h>
+struct ArkUI_UIInputEvent;
+
+QT_BEGIN_NAMESPACE
+
+namespace QOhCompatibility {
+
+inline QStringList& openedLibraries() {
+ static QStringList libs;
+ return libs;
+}
+
+template<typename FuncPtr>
+FuncPtr locationOhosFunction(const QString &name, const QString &lib)
+{
+ if (!openedLibraries().contains(lib)) {
+ auto handle = dlopen(lib.toUtf8().constData(), RTLD_LAZY | RTLD_GLOBAL);
+ if (handle) {
+ openedLibraries() << lib;
+ } else {
+ return nullptr;
+ }
+ }
+ FuncPtr m_function = reinterpret_cast<FuncPtr>(dlsym(RTLD_DEFAULT, name.toUtf8().constData()));
+ if (!m_function) {
+ qWarning() << "Failed to resolve:" << name << ", error:" << dlerror();
+ }
+ return m_function;
+}
+
+template<typename Signature>
+class OhosFunction;
+
+template<typename RetType, typename... Args>
+class OhosFunction<RetType(Args...)> {
+private:
+ using FuncPtr = RetType(*)(Args...);
+ FuncPtr m_function;
+ RetType m_fallbackValue;
+
+public:
+ OhosFunction(const QString &name, const QString &lib, RetType fallback = RetType())
+ : m_fallbackValue(fallback)
+ {
+ m_function = locationOhosFunction<FuncPtr>(name, lib);
+ }
+
+ RetType operator()(const Args&... args) const
+ {
+ if (m_function) {
+ return m_function(args...);
+ }
+ return m_fallbackValue;
+ }
+};
+
+template<typename... Args>
+class OhosFunction<void(Args...)> {
+private:
+ using FuncPtr = void(*)(Args...);
+ FuncPtr m_function;
+
+public:
+ OhosFunction(const QString &name, const QString &lib)
+ {
+ m_function = locationOhosFunction<FuncPtr>(name, lib);
+ }
+
+ void operator()(const Args&... args) const
+ {
+ if (m_function) {
+ m_function(args...);
+ }
+ }
+};
+
+void init();
+
+inline int32_t OH_ArkUI_AxisEvent_GetAxisAction(ArkUI_UIInputEvent * event)
+{
+ static OhosFunction<int32_t(ArkUI_UIInputEvent *)> S_OH_ArkUI_AxisEvent_GetAxisAction("OH_ArkUI_AxisEvent_GetAxisAction",
+ "libace_ndk.z.so", -1);
+ return S_OH_ArkUI_AxisEvent_GetAxisAction(event);
+}
+
+inline NativeDisplayManager_ErrorCode OH_NativeDisplayManager_CaptureScreenPixelmap(uint32_t id, OH_PixelmapNative **out)
+{
+ static OhosFunction<NativeDisplayManager_ErrorCode(uint32_t, OH_PixelmapNative **)> S_OH_NativeDisplayManager_CaptureScreenPixelmap("OH_NativeDisplayManager_CaptureScreenPixelmap",
+ "libnative_display_manager.so", DISPLAY_MANAGER_ERROR_DEVICE_NOT_SUPPORTED);
+ return S_OH_NativeDisplayManager_CaptureScreenPixelmap(id, out);
+}
+
+inline int32_t OH_ArkUI_AxisEvent_GetScrollStep(ArkUI_UIInputEvent *event)
+{
+ static OhosFunction<int32_t(ArkUI_UIInputEvent *)> S_OH_ArkUI_AxisEvent_GetScrollStep("OH_ArkUI_AxisEvent_GetScrollStep",
+ "libace_ndk.z.so", 1);
+ return S_OH_ArkUI_AxisEvent_GetScrollStep(event);
+}
+
+inline OH_UdmfData* OH_Pasteboard_GetDataWithProgress(OH_Pasteboard* pasteboard, Pasteboard_GetDataParams* params,
+ int* status)
+{
+ static OhosFunction<OH_UdmfData *(OH_Pasteboard*, Pasteboard_GetDataParams*,
+ int*)> S_OH_Pasteboard_GetDataWithProgress("OH_Pasteboard_GetDataWithProgress",
+ "libpasteboard.so", nullptr);
+ return S_OH_Pasteboard_GetDataWithProgress(pasteboard, params, status);
+}
+
+inline void OH_Pasteboard_GetDataParams_SetProgressIndicator(Pasteboard_GetDataParams* params,
+ Pasteboard_ProgressIndicator progressIndicator)
+{
+ static OhosFunction<void(Pasteboard_GetDataParams*, Pasteboard_ProgressIndicator)> S_OH_Pasteboard_GetDataParams_SetProgressIndicator("OH_Pasteboard_GetDataParams_SetProgressIndicator",
+ "libpasteboard.so");
+ S_OH_Pasteboard_GetDataParams_SetProgressIndicator(params, progressIndicator);
+}
+
+inline void OH_Pasteboard_GetDataParams_SetProgressListener(Pasteboard_GetDataParams* params,
+ const OH_Pasteboard_ProgressListener listener)
+{
+ static OhosFunction<void(Pasteboard_GetDataParams*, const OH_Pasteboard_ProgressListener)> S_OH_Pasteboard_GetDataParams_SetProgressListener("OH_Pasteboard_GetDataParams_SetProgressListener",
+ "libpasteboard.so");
+ S_OH_Pasteboard_GetDataParams_SetProgressListener(params, listener);
+}
+
+inline int OH_Pasteboard_ProgressInfo_GetProgress(Pasteboard_ProgressInfo* progressInfo)
+{
+ static OhosFunction<int(Pasteboard_ProgressInfo*)> S_OH_Pasteboard_ProgressInfo_GetProgress("OH_Pasteboard_ProgressInfo_GetProgress",
+ "libpasteboard.so", 0);
+ return S_OH_Pasteboard_ProgressInfo_GetProgress(progressInfo);
+}
+
+inline Pasteboard_GetDataParams *OH_Pasteboard_GetDataParams_Create()
+{
+ static OhosFunction<Pasteboard_GetDataParams *()> S_OH_Pasteboard_GetDataParams_Create("OH_Pasteboard_GetDataParams_Create",
+ "libpasteboard.so", nullptr);
+ return S_OH_Pasteboard_GetDataParams_Create();
+}
+
+inline void OH_Pasteboard_ProgressCancel(Pasteboard_GetDataParams* params)
+{
+ static OhosFunction<void(Pasteboard_GetDataParams*)> S_OH_Pasteboard_ProgressCancel("OH_Pasteboard_ProgressCancel",
+ "libpasteboard.so");
+ S_OH_Pasteboard_ProgressCancel(params);
+}
+
+inline int32_t OH_ArkUI_NodeUtils_GetNodeType(ArkUI_NodeHandle node)
+{
+ static OhosFunction<int32_t(ArkUI_NodeHandle)> S_OH_ArkUI_NodeUtils_GetNodeType("OH_ArkUI_NodeUtils_GetNodeType",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_NodeUtils_GetNodeType(node);
+}
+
+inline int32_t OH_ArkUI_UIInputEvent_GetTargetDisplayId(const ArkUI_UIInputEvent* event)
+{
+ static OhosFunction<int32_t(const ArkUI_UIInputEvent*)> S_OH_ArkUI_UIInputEvent_GetTargetDisplayId("OH_ArkUI_UIInputEvent_GetTargetDisplayId",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_UIInputEvent_GetTargetDisplayId(event);
+}
+
+inline int32_t OH_ArkUI_UIInputEvent_GetModifierKeyStates(const ArkUI_UIInputEvent* event, uint64_t* keys)
+{
+ static OhosFunction<int32_t(const ArkUI_UIInputEvent*, uint64_t*)> S_OH_ArkUI_UIInputEvent_GetModifierKeyStates("OH_ArkUI_UIInputEvent_GetModifierKeyStates",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_UIInputEvent_GetModifierKeyStates(event, keys);
+}
+
+inline ArkUI_KeyEventType OH_ArkUI_KeyEvent_GetType(const ArkUI_UIInputEvent* event)
+{
+ static OhosFunction<ArkUI_KeyEventType(const ArkUI_UIInputEvent*)> S_OH_ArkUI_KeyEvent_GetType("OH_ArkUI_KeyEvent_GetType",
+ "libace_ndk.z.so", ARKUI_KEY_EVENT_UNKNOWN);
+ return S_OH_ArkUI_KeyEvent_GetType(event);
+}
+
+inline int32_t OH_ArkUI_KeyEvent_GetKeyCode(const ArkUI_UIInputEvent* event)
+{
+ static OhosFunction<int32_t(const ArkUI_UIInputEvent*)> S_OH_ArkUI_KeyEvent_GetKeyCode("OH_ArkUI_KeyEvent_GetKeyCode",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_KeyEvent_GetKeyCode(event);
+}
+
+inline const char *OH_ArkUI_KeyEvent_GetKeyText(const ArkUI_UIInputEvent* event)
+{
+ static OhosFunction<const char *(const ArkUI_UIInputEvent*)> S_OH_ArkUI_KeyEvent_GetKeyText("OH_ArkUI_KeyEvent_GetKeyText",
+ "libace_ndk.z.so", nullptr);
+ return S_OH_ArkUI_KeyEvent_GetKeyText(event);
+}
+
+inline int32_t OH_ArkUI_UIInputEvent_GetDeviceId(const ArkUI_UIInputEvent* event)
+{
+ static OhosFunction<int32_t(const ArkUI_UIInputEvent*)> S_OH_ArkUI_UIInputEvent_GetDeviceId("OH_ArkUI_UIInputEvent_GetDeviceId",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_UIInputEvent_GetDeviceId(event);
+}
+
+inline int32_t OH_ArkUI_PointerEvent_GetChangedPointerId(const ArkUI_UIInputEvent* event, uint32_t* pointerIndex)
+{
+ static OhosFunction<int32_t(const ArkUI_UIInputEvent*, uint32_t*)> S_OH_ArkUI_PointerEvent_GetChangedPointerId("OH_ArkUI_PointerEvent_GetChangedPointerId",
+ "libace_ndk.z.so", 0);
+ return S_OH_ArkUI_PointerEvent_GetChangedPointerId(event, pointerIndex);
+}
+
+inline WindowManager_ErrorCode OH_NativeWindowManager_UnregisterMouseEventFilter(int32_t windowId)
+{
+ static OhosFunction<WindowManager_ErrorCode(int32_t)> S_OH_NativeWindowManager_UnregisterMouseEventFilter("OH_NativeWindowManager_UnregisterMouseEventFilter",
+ "libnative_window_manager.so", OK);
+ return S_OH_NativeWindowManager_UnregisterMouseEventFilter(windowId);
+}
+
+inline WindowManager_ErrorCode OH_NativeWindowManager_RegisterMouseEventFilter(int32_t windowId,
+ OH_NativeWindowManager_MouseEventFilter mouseEventFilter)
+{
+ static OhosFunction<WindowManager_ErrorCode(int32_t, OH_NativeWindowManager_MouseEventFilter)> S_OH_NativeWindowManager_RegisterMouseEventFilter("OH_NativeWindowManager_RegisterMouseEventFilter",
+ "libnative_window_manager.so", OK);
+ return S_OH_NativeWindowManager_RegisterMouseEventFilter(windowId, mouseEventFilter);
+}
+
+inline WindowManager_ErrorCode OH_NativeWindowManager_RegisterTouchEventFilter(int32_t windowId,
+ OH_NativeWindowManager_TouchEventFilter touchEventFilter)
+{
+ static OhosFunction<WindowManager_ErrorCode(int32_t, OH_NativeWindowManager_TouchEventFilter)> S_OH_NativeWindowManager_RegisterTouchEventFilter("OH_NativeWindowManager_RegisterTouchEventFilter",
+ "libnative_window_manager.so", OK);
+ return S_OH_NativeWindowManager_RegisterTouchEventFilter(windowId, touchEventFilter);
+}
+
+inline int32_t OH_Input_GetMouseEventWindowId(const struct Input_MouseEvent* mouseEvent)
+{
+ static OhosFunction<int32_t(const struct Input_MouseEvent*)> S_OH_Input_GetMouseEventWindowId("OH_Input_GetMouseEventWindowId",
+ "liboh_input.so", 0);
+ return S_OH_Input_GetMouseEventWindowId(mouseEvent);
+}
+
+inline int32_t OH_Input_GetMouseEventDisplayId(const struct Input_MouseEvent* mouseEvent)
+{
+ static OhosFunction<int32_t(const struct Input_MouseEvent*)> S_OH_Input_GetMouseEventDisplayId("OH_Input_GetMouseEventDisplayId",
+ "liboh_input.so", 0);
+ return S_OH_Input_GetMouseEventDisplayId(mouseEvent);
+}
+
+inline int32_t OH_Input_GetTouchEventWindowId(const struct Input_TouchEvent* touchEvent)
+{
+ static OhosFunction<int32_t(const struct Input_TouchEvent*)> S_OH_Input_GetTouchEventWindowId("OH_Input_GetTouchEventWindowId",
+ "liboh_input.so", 0);
+ return S_OH_Input_GetTouchEventWindowId(touchEvent);
+}
+
+inline int32_t OH_Input_GetTouchEventDisplayId(const struct Input_TouchEvent* touchEvent)
+{
+ static OhosFunction<int32_t(const struct Input_TouchEvent*)> S_OH_Input_GetTouchEventDisplayId("OH_Input_GetTouchEventDisplayId",
+ "liboh_input.so", 0);
+ return S_OH_Input_GetTouchEventDisplayId(touchEvent);
+}
+
+inline NativeDisplayManager_ErrorCode OH_NativeDisplayManager_CreateDisplayById(uint32_t displayId,
+ NativeDisplayManager_DisplayInfo **displayInfo)
+{
+ static OhosFunction<NativeDisplayManager_ErrorCode(uint32_t, NativeDisplayManager_DisplayInfo **)> S_OH_NativeDisplayManager_CreateDisplayById("OH_NativeDisplayManager_CreateDisplayById",
+ "libnative_display_manager.so", DISPLAY_MANAGER_ERROR_NOT_SYSTEM_APP);
+ return S_OH_NativeDisplayManager_CreateDisplayById(displayId, displayInfo);
+}
+
+} // QOhCompatibility
+
+QT_END_NAMESPACE
+
+#endif // QOHCOMPATIBILITY_H
new file mode 100644
@@ -0,0 +1,18 @@
+#include "qohdesktopwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhDesktopWindow::QOhDesktopWindow(QWindow *window)
+ : QPlatformWindow(window)
+{
+
+}
+
+WId QOhDesktopWindow::winId() const
+{
+ static int index = 1;
+ return WId(index++);
+}
+
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,17 @@
+#ifndef QOHDESKTOPWINDOW_H
+#define QOHDESKTOPWINDOW_H
+
+#include <qpa/qplatformwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhDesktopWindow : public QPlatformWindow
+{
+public:
+ explicit QOhDesktopWindow(QWindow *window);
+
+ WId winId() const override;
+};
+
+QT_END_NAMESPACE
+#endif // QOHDESKTOPWINDOW_H
new file mode 100644
@@ -0,0 +1,395 @@
+#include <QDebug>
+#include <QTimer>
+#include <QJsModule>
+#include <qohutility.h>
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include <window_manager/oh_display_manager.h>
+
+#include "qohdisplay.h"
+#include "qohauxiliary.h"
+#include "qohjsonlistener.h"
+#include "qohdisplaylistener.h"
+
+QT_BEGIN_NAMESPACE
+
+#if OHOS_SDK_VERSION >= 20
+QOhDisplay::QOhDisplay(uint32_t id, QObject *parent)
+ : QObject(parent)
+ , m_availableRect()
+ , m_screen(nullptr)
+ , m_info(nullptr)
+ , m_pos(QPoint(0, 0))
+{
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateDisplayById(id, &m_info);
+ if (code != DISPLAY_MANAGER_OK || m_info == nullptr) {
+ m_info = nullptr;
+ qWarning() << "create display by id failed: " << code;
+ return;
+ }
+
+ /* FIXME wanghao 如果是折叠态需要加上中轴宽度到B面 */
+ if (m_info->id == 0 && m_info->width == 2472 && m_info->height == 1608) {
+ m_info->height += 80;
+ }
+
+ m_oldScaledDensity = scaledDensity();
+ m_pos = getPosition();
+ m_availableRect = this->getAvailableArea();
+}
+#else
+QOhDisplay::QOhDisplay(uint32_t id, const QPoint &pos, QObject *parent)
+ : QObject(parent)
+ , m_availableRect()
+ , m_screen(nullptr)
+ , m_info(nullptr)
+ , m_pos(pos)
+ , m_display(nullptr)
+ , m_availableAreaChangeListener(nullptr)
+{
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateDisplayById(id, &m_info);
+ if (code != DISPLAY_MANAGER_OK || m_info == nullptr) {
+ qWarning() << "create display by id failed: " << code;
+ return;
+ }
+
+ /* FIXME wanghao 如果是折叠态需要加上中轴宽度到B面 */
+ if (m_info->id == 0 && m_info->width == 2472 && m_info->height == 1608) {
+ m_info->height += 80;
+ }
+
+ m_oldScaledDensity = scaledDensity();
+
+ // C接口没有监听可用区域变化的接口,使用js对象
+ QtOh::runOnJsUIThreadAndWait([this, id]() {
+ QJsModule display("@ohos.display");
+ Napi::Value value = display.call("getDisplayByIdSync", { Napi::Number::New(display.env(), id) });
+ if (value.IsObject()) {
+ m_display.reset(new QJsObject(value.As<Napi::Object>()));
+ } else {
+ qCritical("cannot get display by %d", id);
+ return;
+ }
+
+ m_availableAreaChangeListener = new QOhJsOnListener(m_display.data(), "availableAreaChange", [this](const Napi::CallbackInfo& info) {
+ Napi::Object rect = info[0].As<Napi::Object>();
+ QRect qtRect = QtOh::jsRect2QRect(rect);
+
+ m_availableRect = qtRect;
+ emit this->availableGeometryChanged();
+ });
+ m_availableAreaChangeListener->on();
+ });
+ m_availableRect = this->getAvailableArea();
+}
+
+#endif
+QOhDisplay::~QOhDisplay()
+{
+#if OHOS_SDK_VERSION < 20
+ if (m_availableAreaChangeListener) {
+ m_availableAreaChangeListener->off();
+ delete m_availableAreaChangeListener;
+ }
+#endif
+
+ if (m_info != nullptr)
+ OH_NativeDisplayManager_DestroyDisplay(m_info);
+}
+
+void QOhDisplay::setScreen(QPlatformScreen *screen)
+{
+ m_screen = screen;
+}
+
+QPlatformScreen *QOhDisplay::screen() const
+{
+ return m_screen;
+}
+
+QPoint QOhDisplay::position() const
+{
+ return m_pos;
+}
+
+void QOhDisplay::setPosition(const QPoint &p)
+{
+ m_pos = p;
+}
+
+void QOhDisplay::setPrimary()
+{
+ m_isPrimary = true;
+}
+
+bool QOhDisplay::isPrimary() const
+{
+ return m_isPrimary;
+}
+
+bool QOhDisplay::isValid() const
+{
+ return m_info != nullptr;
+}
+
+uint32_t QOhDisplay::id() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->id;
+}
+
+QString QOhDisplay::name() const
+{
+ if (nullptr == m_info)
+ return QString();
+
+ return QString::fromLocal8Bit(m_info->name);
+}
+
+Qt::ScreenOrientation QOhDisplay::orientation() const
+{
+ if (m_info == nullptr)
+ return Qt::LandscapeOrientation;
+ switch (m_info->orientation) {
+ case DISPLAY_MANAGER_PORTRAIT:
+ return Qt::PortraitOrientation;
+ break;
+ case DISPLAY_MANAGER_LANDSCAPE:
+ return Qt::LandscapeOrientation;
+ break;
+ case DISPLAY_MANAGER_PORTRAIT_INVERTED:
+ return Qt::InvertedPortraitOrientation;
+ break;
+ case DISPLAY_MANAGER_LANDSCAPE_INVERTED:
+ return Qt::InvertedLandscapeOrientation;
+ break;
+ default:
+ return Qt::LandscapeOrientation;
+ }
+}
+
+QSize QOhDisplay::size() const
+{
+ if (m_info == nullptr)
+ return QSize();
+ return QSize(m_info->width, m_info->height);
+}
+
+QRect QOhDisplay::geometry() const
+{
+ if (m_info == nullptr)
+ return QRect();
+ return QRect(m_pos, QSize(m_info->width, m_info->height));
+}
+
+QRect QOhDisplay::availableGeometry() const
+{
+ return m_availableRect.translated(m_pos);
+}
+
+QSize QOhDisplay::physicalSize() const
+{
+ if (m_info == nullptr)
+ return QSize();
+ return QSize(m_info->physicalWidth, m_info->physicalHeight);
+}
+
+qreal QOhDisplay::densityPixels() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->densityPixels;
+}
+
+qreal QOhDisplay::scaledDensity() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->scaledDensity;
+}
+
+qreal QOhDisplay::oldScaledDensity() const
+{
+ return m_oldScaledDensity;
+}
+
+qreal QOhDisplay::pixelDensity() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->densityDPI / 160; // 96?
+}
+
+qreal QOhDisplay::densityDPI() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->densityDPI;
+}
+
+qreal QOhDisplay::xDPI() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->xDPI;
+}
+
+qreal QOhDisplay::yDPI() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->yDPI;
+}
+
+int QOhDisplay::refreshRate() const
+{
+ if (m_info == nullptr)
+ return 0;
+ return m_info->refreshRate;
+}
+
+#if OHOS_SDK_VERSION >= 20
+void QOhDisplay::reObtainInfo(uint32_t id)
+{
+ Qt::ScreenOrientation old_orientation = orientation();
+
+ QRect old_geometry = geometry();
+ QSize old_physicalSize = physicalSize();
+ qreal old_scaledDensity = scaledDensity();
+ int old_refreshRate = refreshRate();
+
+ if (m_info != nullptr) {
+ OH_NativeDisplayManager_DestroyDisplay(m_info);
+ m_info = nullptr;
+ }
+
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateDisplayById(id, &m_info);
+ if (code != DISPLAY_MANAGER_OK || m_info == nullptr) {
+ qWarning() << "create display by id failed: " << code;
+ }
+ m_pos = getPosition();
+
+ if (old_orientation != orientation())
+ emit orientationChanged();
+
+ if (old_refreshRate != refreshRate())
+ emit refreshRateChanged();
+
+ if (physicalSize() != old_physicalSize)
+ emit physicalSizeChanged();
+
+ if (old_geometry != geometry())
+ emit geometryChanged();
+
+ if (!qFuzzyCompare(old_scaledDensity, scaledDensity())) {
+ m_oldScaledDensity = old_scaledDensity;
+ emit scaledDensityChanged();
+ }
+}
+
+QPoint QOhDisplay::getPosition() const
+{
+ if (m_info == nullptr)
+ return QPoint();
+ NativeDisplayManager_SourceMode mode = DISPLAY_SOURCE_MODE_NONE;
+ auto code = OH_NativeDisplayManager_GetDisplaySourceMode(m_info->id, &mode);
+ if (code == DISPLAY_MANAGER_OK) {
+ if ((mode == DISPLAY_SOURCE_MODE_MAIN)
+ || (mode == DISPLAY_SOURCE_MODE_EXTEND)) {
+ int x = 0;
+ int y = 0;
+ OH_NativeDisplayManager_GetDisplayPosition(m_info->id, &x, &y);
+ return QPoint(x, y);
+ }
+ }
+ return QPoint();
+}
+void QOhDisplay::checkAvailableGeometry()
+{
+ QRect old = m_availableRect;
+ m_availableRect = getAvailableArea();
+ if (old != m_availableRect)
+ emit this->availableGeometryChanged();
+}
+#else
+void QOhDisplay::reObtainInfo(uint32_t id, const QPoint &pos)
+{
+ Qt::ScreenOrientation old_orientation = orientation();
+ QRect old_geometry = geometry();
+ QSize old_physicalSize = physicalSize();
+ qreal old_scaledDensity = scaledDensity();
+ int old_refreshRate = refreshRate();
+
+ if (m_info != nullptr) {
+ OH_NativeDisplayManager_DestroyDisplay(m_info);
+ m_info = nullptr;
+ }
+
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateDisplayById(id, &m_info);
+ if (code != DISPLAY_MANAGER_OK || m_info == nullptr) {
+ qWarning() << "create display by id failed: " << code;
+ }
+ if (m_pos != pos) {
+ m_pos = pos;
+ }
+
+ if (old_orientation != orientation())
+ emit orientationChanged();
+
+ if (old_refreshRate != refreshRate())
+ emit refreshRateChanged();
+
+ if (physicalSize() != old_physicalSize)
+ emit physicalSizeChanged();
+
+ if (old_geometry != geometry())
+ emit geometryChanged();
+
+ if (!qFuzzyCompare(old_scaledDensity, scaledDensity())) {
+ m_oldScaledDensity = old_scaledDensity;
+ emit scaledDensityChanged();
+ }
+}
+#endif
+
+QRect QOhDisplay::getAvailableArea() const
+{
+ QRect area;
+ const QtOh::Utility::DeviceType deviceType = QtOh::Utility::type();
+ if (deviceType == QtOh::Utility::PC || deviceType == QtOh::Utility::Tablet) {
+#if OHOS_SDK_VERSION >= 20
+ NativeDisplayManager_Rect *rawRect = nullptr;
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateAvailableArea(m_info->id, &rawRect);
+ if (rawRect != nullptr && code == DISPLAY_MANAGER_OK) {
+ area = QRect(rawRect->left, rawRect->top, rawRect->width, rawRect->height);
+ OH_NativeDisplayManager_DestroyAvailableArea(rawRect);
+ }
+#else
+ if (!m_display) {
+ return {};
+ }
+ area = QtOh::runOnJsUIThreadWithPromise<QRect>([this](auto p) {
+ auto result = m_display->call("getAvailableArea");
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo &info) {
+ Napi::Object rect = info[0].As<Napi::Object>();
+ p->set_value(QtOh::jsRect2QRect(rect));
+ }).onCatch([p](const Napi::CallbackInfo &info) {
+ int code = info[0].As<Napi::Object>().Get("code").ToNumber();
+ qWarning() << "getAvailableArea failed: " << code;
+ p->set_value(QRect());
+ });
+ });
+ if (area.width() == 0 || area.height() == 0) {
+ area = QRect(0, 0, m_info->width, m_info->height);
+ }
+#endif
+ } else {
+ area = QRect(0, 0, m_info->width, m_info->height);
+ }
+
+ return area;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,84 @@
+#ifndef QOHDISPLAY_H
+#define QOHDISPLAY_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qrect.h>
+#include <window_manager/oh_display_info.h>
+QT_BEGIN_NAMESPACE
+class QPlatformScreen;
+
+#if OHOS_SDK_VERSION >= 20
+#else
+class QJsObject;
+class QOhJsOnListener;
+#endif
+
+class QOhDisplay : public QObject
+{
+ Q_OBJECT
+public:
+#if OHOS_SDK_VERSION >= 20
+ explicit QOhDisplay(uint32_t id, QObject *parent = 0);
+#endif
+ explicit QOhDisplay(uint32_t id, const QPoint &pos, QObject *parent = 0);
+ ~QOhDisplay();
+
+ void setScreen(QPlatformScreen *screen);
+ QPlatformScreen *screen() const;
+
+ QPoint position() const;
+ void setPosition(const QPoint &p);
+ void setPrimary();
+ bool isPrimary() const;
+ bool isValid() const;
+ uint32_t id() const;
+ QString name() const;
+ Qt::ScreenOrientation orientation() const;
+ QSize size() const;
+ QRect geometry() const;
+ QRect availableGeometry() const;
+ QSize physicalSize() const;
+ qreal densityPixels() const;
+ qreal scaledDensity() const;
+ qreal oldScaledDensity() const;
+ qreal pixelDensity() const;
+ qreal densityDPI() const;
+ qreal xDPI() const;
+ qreal yDPI() const;
+ int refreshRate() const;
+#if OHOS_SDK_VERSION >= 20
+ void reObtainInfo(uint32_t id);
+ void checkAvailableGeometry();
+#else
+ void reObtainInfo(uint32_t id, const QPoint &pos);
+#endif
+Q_SIGNALS:
+ void orientationChanged();
+ void geometryChanged();
+ void availableGeometryChanged();
+ void physicalSizeChanged();
+ void densityPixelsChanged();
+ void scaledDensityChanged();
+ void pixelDensityChanged();
+ void refreshRateChanged();
+
+private:
+#if OHOS_SDK_VERSION >= 20
+ QPoint getPosition() const;
+#endif
+ QRect getAvailableArea() const;
+ QRect m_availableRect;
+ QPlatformScreen *m_screen;
+ NativeDisplayManager_DisplayInfo *m_info = nullptr;
+ bool m_isPrimary = false;
+ qreal m_oldScaledDensity = 0;
+ QPoint m_pos;
+#if OHOS_SDK_VERSION < 20
+ QScopedPointer<QJsObject> m_display;
+ QOhJsOnListener *m_availableAreaChangeListener;
+#endif
+};
+
+QT_END_NAMESPACE
+#endif // QOHDISPLAY_H
new file mode 100644
@@ -0,0 +1,4 @@
+#include "qohdisplaylistener.h"
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,50 @@
+#ifndef QOHDISPLAYLISTENER_H
+#define QOHDISPLAYLISTENER_H
+
+#include <QDebug>
+#include <window_manager/oh_display_info.h>
+
+QT_BEGIN_NAMESPACE
+class QOhAbstractDisplayListener
+{
+public:
+ virtual ~QOhAbstractDisplayListener()
+ {
+
+ }
+};
+
+template <typename... Args>
+using ListenerCallback = void(*)(Args...);
+
+template <typename... Args>
+class QOhDisplayListener : public QOhAbstractDisplayListener
+{
+public:
+ QOhDisplayListener(NativeDisplayManager_ErrorCode (*RegisterFunction)(ListenerCallback<Args...>, uint32_t*),
+ NativeDisplayManager_ErrorCode (*UnregisterFunction)(uint32_t),
+ ListenerCallback<Args...> callback)
+ : m_unregisterFunction(UnregisterFunction)
+ {
+ NativeDisplayManager_ErrorCode code = RegisterFunction(callback, &m_listenerIndex);
+
+ if (code != DISPLAY_MANAGER_OK) {
+ qWarning() << "registration failed with code:" << code;
+ m_listenerIndex = 0;
+ }
+ }
+
+ ~QOhDisplayListener() {
+ if (m_listenerIndex != 0 && m_unregisterFunction) {
+ m_unregisterFunction(m_listenerIndex);
+ m_listenerIndex = 0;
+ }
+ }
+
+private:
+ uint32_t m_listenerIndex = 0;
+ NativeDisplayManager_ErrorCode (*m_unregisterFunction)(uint32_t);
+};
+
+QT_END_NAMESPACE
+#endif // QOHDISPLAYLISTENER_H
new file mode 100644
@@ -0,0 +1,485 @@
+#include <QDebug>
+#include <QHash>
+#include <QTimer>
+#include <QScopeGuard>
+#include <QRegularExpression>
+#include <QRegularExpressionMatchIterator>
+
+#include <private/qobject_p.h>
+#include <qopenharmonydefines.h>
+#include <private/qopenharmony_p.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <window_manager/oh_display_manager.h>
+
+
+#include "qohmain.h"
+#include "qjsdisplay.h"
+#include "qohdisplay.h"
+#include "qjscontext.h"
+#include "qjssettings.h"
+#include "qohauxiliary.h"
+#include "qohnormalwindow.h"
+#include "qohdisplaymanager.h"
+#include "qohplatformscreen.h"
+#include "qohdisplaylistener.h"
+#include "jsclass/qjscontext.h"
+#include "qohnativewindowmanager.h"
+#include "jsclass/qjswindowstage.h"
+#include "qohplatformopenglwindow.h"
+
+QT_BEGIN_NAMESPACE
+const static QString DisplayPositionKey = "user_set_relative_position";
+
+class QOhDisplayManagerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QOhDisplayManager)
+ Q_DISABLE_COPY_MOVE(QOhDisplayManagerPrivate)
+
+public:
+ QOhDisplayManagerPrivate();
+ ~QOhDisplayManagerPrivate();
+
+ static QOhDisplayManagerPrivate* get(QOhDisplayManager*q) {return q->d_func();}
+ static QOhDisplayId displayIdForMainWindow();
+protected:
+ void init();
+ static void displayChangeCallback(QOhDisplayId displayId);
+ void handleDisplayChange(QOhDisplayId displayId);
+ void handleDisplayAdd(QOhDisplayId displayId);
+ void handleDisplayRemove(QOhDisplayId displayId);
+#if OHOS_SDK_VERSION >= 20
+ void handleDisplayAvailableAreaChange(QOhDisplayId displayId);
+#endif
+ static bool isVirtualDisplay(QOhDisplayId displayId) { return displayId >= 1000; }
+ void addDisplay(QOhDisplayId displayId, bool isPrimary);
+
+ static void foldDisplayModeChangeCallback(NativeDisplayManager_FoldDisplayMode displayMode);
+
+#if OHOS_SDK_VERSION < 20
+ QHash<uint64_t, QPoint> getDisplayPosition();
+ QPoint getDisplayPosition(QOhDisplayId displayId) const;
+ void listenDisplayPosition();
+#endif
+
+ class QOhPlatformScreenHolder
+ {
+ public:
+ explicit QOhPlatformScreenHolder(QOhDisplay*display, bool primary = false);
+
+ QOhPlatformScreenHolder(const QOhPlatformScreenHolder &) = delete;
+ QOhPlatformScreenHolder(QOhPlatformScreenHolder &&) = delete;
+ QOhPlatformScreenHolder &operator=(const QOhPlatformScreenHolder &) = delete;
+ QOhPlatformScreenHolder &operator=(QOhPlatformScreenHolder &&) = delete;
+
+ ~QOhPlatformScreenHolder();
+
+ QOhPlatformScreen *platformScreenOrNull() const;
+
+ private:
+ QOhPlatformScreen* m_platformScreen;
+ };
+
+ std::map<QOhDisplayId, std::unique_ptr<QOhPlatformScreenHolder>> m_displays;
+ QList<QOhAbstractDisplayListener *> m_listeners;
+#if OHOS_SDK_VERSION < 20
+ std::shared_ptr<QJsDisplay> m_jsDisplay;
+ QHash<uint64_t, QPoint> m_displayPosition;
+#endif
+};
+
+QOhDisplayManagerPrivate::QOhPlatformScreenHolder::QOhPlatformScreenHolder(QOhDisplay*display, bool primary)
+{
+ auto screen = std::make_unique<QOhPlatformScreen>(display);
+ m_platformScreen = screen.get();
+ QWindowSystemInterface::handleScreenAdded(screen.release(), primary);
+}
+
+QOhDisplayManagerPrivate::QOhPlatformScreenHolder::~QOhPlatformScreenHolder()
+{
+ if (m_platformScreen != nullptr) {
+ QWindowSystemInterface::handleScreenRemoved(m_platformScreen);
+ }
+}
+
+QOhPlatformScreen *QOhDisplayManagerPrivate::QOhPlatformScreenHolder::platformScreenOrNull() const
+{
+ return m_platformScreen;
+}
+
+QOhDisplayManagerPrivate::QOhDisplayManagerPrivate()
+{
+#if OHOS_SDK_VERSION < 20
+ m_jsDisplay = std::make_shared<QJsDisplay>();
+#endif
+}
+
+QOhDisplayManagerPrivate::~QOhDisplayManagerPrivate()
+{
+ qDeleteAll(m_listeners);
+ m_listeners.clear();
+}
+
+QOhDisplayId QOhDisplayManagerPrivate::displayIdForMainWindow()
+{
+ QOhNativeWindowManager *inst = QOhNativeWindowManager::instance();
+ auto ctx = inst->context();
+ if (!ctx || (ctx->contextType() != QJsContext::UIContext)) {
+ return QOhDisplayManager::m_startDisplayId;
+ }
+ auto stage = ctx->windowStage();
+ if (!stage) {
+ return QOhDisplayManager::m_startDisplayId;
+ }
+ return QtOh::runOnJsUIThreadWithResult([ctx]{
+ Napi::Object obj = ctx->windowStage()->call("getMainWindowSync").ToObject();
+ if (obj.IsEmpty())
+ return QOhDisplayManager::m_startDisplayId;
+ auto prop = QOhNormalWindow::getWindowProperties(obj);
+ return QOhDisplayId(prop.displayId());
+ });
+}
+
+void QOhDisplayManagerPrivate::init()
+{
+#if OHOS_SDK_VERSION >= 20
+ m_listeners << new QOhDisplayListener<uint64_t>(OH_NativeDisplayManager_RegisterDisplayChangeListener,
+ OH_NativeDisplayManager_UnregisterDisplayChangeListener,
+ &QOhDisplayManagerPrivate::displayChangeCallback);
+ m_listeners << new QOhDisplayListener<uint64_t>(OH_NativeDisplayManager_RegisterDisplayAddListener,
+ OH_NativeDisplayManager_UnregisterDisplayAddListener,
+ [](uint64_t displayId){
+ if (QOhDisplayManagerPrivate::isVirtualDisplay(displayId)) {
+ return;
+ }
+ auto manager = QOhDisplayManagerPrivate::get(QOhDisplayManager::instance());
+ QtOh::runOnQtMainThread(&QOhDisplayManagerPrivate::handleDisplayAdd, manager, displayId);
+ });
+ m_listeners << new QOhDisplayListener<uint64_t>(OH_NativeDisplayManager_RegisterDisplayRemoveListener,
+ OH_NativeDisplayManager_UnregisterDisplayRemoveListener,
+ [](uint64_t displayId){
+ if (QOhDisplayManagerPrivate::isVirtualDisplay(displayId)) {
+ return;
+ }
+ auto manager = QOhDisplayManagerPrivate::get(QOhDisplayManager::instance());
+ QtOh::runOnQtMainThread(&QOhDisplayManagerPrivate::handleDisplayRemove, manager, displayId);
+ });
+ m_listeners << new QOhDisplayListener<uint64_t>(OH_NativeDisplayManager_RegisterAvailableAreaChangeListener,
+ OH_NativeDisplayManager_UnregisterAvailableAreaChangeListener,
+ [](uint64_t displayId) {
+ auto manager = QOhDisplayManagerPrivate::get(QOhDisplayManager::instance());
+ QtOh::runOnQtMainThread(&QOhDisplayManagerPrivate::handleDisplayAvailableAreaChange, manager, displayId);
+ });
+#else
+ this->m_displayPosition = this->getDisplayPosition();
+ // 监听屏幕相对位置变化
+ this->listenDisplayPosition();
+ // 由于C-API无法监听屏幕添加、移除,所以这里还是借用JS-API
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_jsDisplay->initialize(QJsDisplay::CreateInfo{
+ .displayChangeCb = &QOhDisplayManagerPrivate::displayChangeCallback,
+ .displayAddedCb = [this](uint64_t displayId) {
+ if (isVirtualDisplay(displayId)) {
+ return;
+ }
+ QtOh::runOnQtMainThread(&QOhDisplayManagerPrivate::handleDisplayAdd, this, displayId);
+ },
+ .displayRemovedCb = [this](uint64_t displayId) {
+ if (isVirtualDisplay(displayId)) {
+ return;
+ }
+ QtOh::runOnQtMainThread(&QOhDisplayManagerPrivate::handleDisplayRemove, this, displayId);
+ },
+ });
+ });
+#endif
+ /* 折叠屏 */
+ if (OH_NativeDisplayManager_IsFoldable()) {
+ m_listeners << new QOhDisplayListener<NativeDisplayManager_FoldDisplayMode>(OH_NativeDisplayManager_RegisterFoldDisplayModeChangeListener,
+ OH_NativeDisplayManager_UnregisterFoldDisplayModeChangeListener,
+ &QOhDisplayManagerPrivate::foldDisplayModeChangeCallback);
+ }
+}
+
+void QOhDisplayManagerPrivate::handleDisplayChange(QOhDisplayId displayId)
+{
+ Q_Q(QOhDisplayManager);
+ QTimer::singleShot(200, q, [this, displayId]{
+ auto iter = m_displays.find(displayId);
+ if (iter == m_displays.end()) {
+ LOGW("handleDisplayChange cannot find display displayId=%{public}lu.", displayId);
+ return;
+ }
+ LOGI("handleDisplayChange displayId=%{public}lu.", displayId);
+ auto &holder = iter->second;
+ auto *platformScreen = holder->platformScreenOrNull();
+ if (platformScreen) {
+ QOhDisplay *display = platformScreen->display();
+ if (display != nullptr) {
+#if OHOS_SDK_VERSION >= 20
+ display->reObtainInfo(displayId);
+#else
+ QPoint p = getDisplayPosition(displayId);
+ display->reObtainInfo(displayId, p);
+#endif
+ }
+ }
+ });
+}
+
+void QOhDisplayManagerPrivate::handleDisplayAdd(QOhDisplayId displayId)
+{
+ auto iter = m_displays.find(displayId);
+ if (iter != m_displays.end()) {
+ LOGW("handleDisplayAdd duplicated displayId=%{public}lu.", displayId);
+ return;
+ }
+ addDisplay(displayId, false);
+}
+
+void QOhDisplayManagerPrivate::handleDisplayRemove(QOhDisplayId displayId)
+{
+ auto iter = m_displays.find(displayId);
+ if (iter == m_displays.end()) {
+ LOGW("handleDisplayChange cannot find display displayId=%{public}lu.", displayId);
+ return;
+ }
+ m_displays.erase(iter);
+ if (displayId == QOhDisplayManager::m_startDisplayId) {
+ NativeDisplayManager_DisplayInfo *displayInfo = nullptr;
+ if (DISPLAY_MANAGER_OK == OH_NativeDisplayManager_CreatePrimaryDisplay(&displayInfo)) {
+ QOhDisplayManager::m_startDisplayId = displayInfo->id;
+ }
+ if (displayInfo != nullptr) {
+ OH_NativeDisplayManager_DestroyDisplay(displayInfo);
+ }
+ }
+}
+
+#if OHOS_SDK_VERSION >= 20
+void QOhDisplayManagerPrivate::handleDisplayAvailableAreaChange(QOhDisplayId displayId)
+{
+ auto iter = m_displays.find(displayId);
+ if (iter == m_displays.end()) {
+ LOGW("handleDisplayAvailableAreaChange cannot find display displayId=%{public}lu.", displayId);
+ return;
+ }
+ auto &holder = iter->second;
+ auto *platformScreen = holder->platformScreenOrNull();
+ if (platformScreen) {
+ QOhDisplay *display = platformScreen->display();
+ if (display != nullptr) {
+ display->checkAvailableGeometry();
+ }
+ }
+}
+#endif
+
+void QOhDisplayManagerPrivate::addDisplay(QOhDisplayId displayId, bool isPrimary)
+{
+#if OHOS_SDK_VERSION >= 20
+ NativeDisplayManager_SourceMode mode = DISPLAY_SOURCE_MODE_NONE;
+ auto code = OH_NativeDisplayManager_GetDisplaySourceMode(displayId, &mode);
+ QScopedPointer<QOhDisplay> display;
+ if (code == DISPLAY_MANAGER_OK) {
+ if ((mode == DISPLAY_SOURCE_MODE_MAIN)
+ || (mode == DISPLAY_SOURCE_MODE_EXTEND)) {
+ display.reset(new QOhDisplay(displayId));
+ }
+ }
+#else
+ QPoint p = getDisplayPosition(displayId);
+ QScopedPointer<QOhDisplay> display(new QOhDisplay(displayId, p));
+#endif
+ if (display.isNull() || !display->isValid()) {
+ qWarning() << Q_FUNC_INFO << "failed";
+ return;
+ }
+ if (isPrimary) {
+ display->setPrimary();
+ }
+ m_displays.emplace(displayId, std::make_unique<QOhDisplayManagerPrivate::QOhPlatformScreenHolder>(display.take(), isPrimary));
+}
+
+void QOhDisplayManagerPrivate::displayChangeCallback(uint64_t displayId)
+{
+ QtOh::runOnQtMainThread([displayId] {
+ QOhDisplayManagerPrivate *d = QOhDisplayManagerPrivate::get(QOhDisplayManager::instance());
+ d->handleDisplayChange(displayId);
+ });
+}
+
+void QOhDisplayManagerPrivate::foldDisplayModeChangeCallback(NativeDisplayManager_FoldDisplayMode displayMode)
+{
+ // 暂时没什么用,某设备无法触发这个
+ // for (QOhDisplay* display : qAsConst(m_allDisplay)) {
+ // QtOh::runOnQtMainThread([display] {
+ // display->reObtainInfo(display->id());
+ // });
+ // }
+ LOGI("foldDisplayModeChangeCallback mode: [%{public}d]", displayMode);
+}
+
+#if OHOS_SDK_VERSION < 20
+QHash<uint64_t, QPoint> QOhDisplayManagerPrivate::getDisplayPosition()
+{
+ QString positionValue = QJsSettings::instance()->getValueFromUserDomain(DisplayPositionKey);
+ if (positionValue.isEmpty())
+ return {};
+ QRegularExpression regex("\"\\s*(\\d+\\s+\\d+\\s+\\d+)\\s*\"");
+ regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpressionMatchIterator matches = regex.globalMatch(positionValue);
+
+ QHash<uint64_t, QPoint> result;
+ while (matches.hasNext()) {
+ QRegularExpressionMatch match = matches.next();
+ QString numStr = match.captured(1);
+
+ // 使用QString分割数字
+ QStringList parts = numStr.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
+ if (parts.size() == 3) {
+ bool ok[3];
+ int displayId = parts[0].toInt(&ok[0]);
+ int posX = parts[1].toInt(&ok[1]);
+ int posY = parts[2].toInt(&ok[2]);
+
+ if (ok[0] && ok[1] && ok[2]) {
+ result.insert(displayId, QPoint(posX, posY));
+ }
+ }
+ }
+ return result;
+}
+
+QPoint QOhDisplayManagerPrivate::getDisplayPosition(QOhDisplayId displayId) const
+{
+ if (m_displayPosition.contains(displayId)) {
+ return m_displayPosition.value(displayId, QPoint());
+ }
+
+ if (isVirtualDisplay(displayId) || displayId == 0) {
+ return QPoint();
+ }
+ // 对于不存在的displayId,尝试通过规则猜测扩展屏幕的位置
+ NativeDisplayManager_DisplayInfo *info = nullptr;
+ auto free = qScopeGuard([&info]() {
+ if (info) {
+ OH_NativeDisplayManager_DestroyDisplay(info);
+ }
+ });
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateDisplayById(0, &info);
+ if (code == 0) {
+ // 根据0号屏幕的方向,猜测扩展屏幕的位置
+ if (info->orientation == DISPLAY_MANAGER_LANDSCAPE
+ || info->orientation == DISPLAY_MANAGER_LANDSCAPE_INVERTED) {
+ return QPoint(info->width, 0);
+ } else {
+ return QPoint(0, info->height + 80); /* FIXME wanghao 折叠设备中间屏位置的临时处理 */
+ }
+ }
+ return QPoint();
+}
+
+void QOhDisplayManagerPrivate::listenDisplayPosition()
+{
+ QJsSettings::instance()->registerKeyObserver("user_set_relative_position", [this]() {
+ //in js thread
+ QtOh::runOnQtMainThread([this]{
+ this->m_displayPosition = this->getDisplayPosition();
+ // notify display change, refresh all displays
+ // 用户更改配置后,有时候会引发单独一个屏幕变化,有时候会导致所有屏幕变化
+ // 我们可以判断一下具体哪个屏幕的offset变化了
+ for(auto iter = m_displayPosition.begin(); iter != m_displayPosition.end(); iter++) {
+ auto displayId = iter.key();
+ this->handleDisplayChange(displayId);
+ }
+ });
+ });
+}
+#endif
+
+QOhDisplayManager::QOhDisplayManager(QObject *parent)
+ : QObject(*new QOhDisplayManagerPrivate(), parent)
+{
+ m_self = this;
+}
+
+QOhDisplayManager::~QOhDisplayManager()
+{
+ m_self = nullptr;
+}
+
+QOhDisplayManager *QOhDisplayManager::instance()
+{
+ return m_self;
+}
+
+QOhPlatformScreen *QOhDisplayManager::startScreen()
+{
+ return QOhDisplayManager::instance()->platformScreenOrNull(QOhDisplayManager::m_startDisplayId);
+}
+
+void QOhDisplayManager::init()
+{
+ Q_D(QOhDisplayManager);
+ d->init();
+ NativeDisplayManager_DisplaysInfo *all = nullptr;
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CreateAllDisplays(&all);
+ if (code != DISPLAY_MANAGER_OK || all == nullptr) {
+ qWarning() << "create all displays failed: " << code;
+ return;
+ }
+
+ auto freeGuard = qScopeGuard([all]() {
+ OH_NativeDisplayManager_DestroyAllDisplays(all);
+ });
+
+ if (all->displaysLength == 0)
+ return;
+
+ // 监听扩展屏相对位置变化
+ uint64_t startId = d->displayIdForMainWindow();
+ m_startDisplayId = startId;
+ qWarning() << Q_FUNC_INFO << "startId" << startId;
+ for (uint32_t i = 0; i < all->displaysLength; ++i) {
+ uint32_t id = all->displaysInfo[i].id;
+ d->addDisplay(id, id == 0);
+ }
+}
+
+QList<QOhPlatformScreen*> QOhDisplayManager::allScreens()
+{
+ Q_D(QOhDisplayManager);
+ QList<QOhPlatformScreen *> screens;
+ for (auto &iter : d->m_displays) {
+ auto &holder = iter.second;
+ QOhPlatformScreen *platformScreen = holder->platformScreenOrNull();
+ if (platformScreen) {
+ screens << platformScreen;
+ }
+ }
+
+ return screens;
+}
+
+QOhPlatformScreen* QOhDisplayManager::platformScreenOrNull(QOhDisplayId displayId) const
+{
+ Q_D(const QOhDisplayManager);
+ auto iter = d->m_displays.find(displayId);
+ return iter == d->m_displays.end() ? nullptr : iter->second->platformScreenOrNull();
+}
+
+QOhPlatformScreen *QOhDisplayManager::screenAt(const QPoint &point)
+{
+ Q_D(QOhDisplayManager);
+ for (auto &iter : d->m_displays) {
+ auto &holder = iter.second;
+ QOhPlatformScreen *platformScreen = holder->platformScreenOrNull();
+ if (platformScreen && platformScreen->geometry().contains(point)) {
+ return platformScreen;
+ }
+ }
+ return nullptr;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,36 @@
+#ifndef QOHDISPLAYMANAGER_H
+#define QOHDISPLAYMANAGER_H
+
+#include <QtCore/qobject.h>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+class QOhDisplay;
+class QJsDisplay;
+class QOhPlatformScreen;
+class QOhDisplayManagerPrivate;
+typedef uint64_t QOhDisplayId;
+class QOhDisplayManager : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOhDisplayManager)
+ Q_DISABLE_COPY_MOVE(QOhDisplayManager)
+
+ static inline QOhDisplayManager *m_self = nullptr;
+
+public:
+ explicit QOhDisplayManager(QObject *parent = 0);
+ ~QOhDisplayManager();
+
+ void init();
+ static QOhDisplayManager *instance();
+ static QOhPlatformScreen *startScreen();
+ QList<QOhPlatformScreen*> allScreens();
+ QOhPlatformScreen* platformScreenOrNull(QOhDisplayId) const;
+ QOhPlatformScreen* screenAt(const QPoint &point);
+private:
+ static inline QOhDisplayId m_startDisplayId = 0;
+};
+
+QT_END_NAMESPACE
+#endif // QOHDISPLAYMANAGER_H
new file mode 100644
@@ -0,0 +1,392 @@
+#include "qohdrag.h"
+#include "qohmain.h"
+#include "qohdisplay.h"
+#include "qohauxiliary.h"
+#include "qohdragevent.h"
+#include "qohwindownode.h"
+#include "qohplatformwindow.h"
+#include <QMutex>
+#include <QSemaphore>
+#include <QScreen>
+#include <qopenharmonydefines.h>
+#include <QtGui/qguiapplication.h>
+#include <QtCore/QLoggingCategory>
+#include <private/qhighdpiscaling_p.h>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/qmimedata.h>
+#include <QtCore/qscopeguard.h>
+#include <QtGui/qpainter.h>
+#include <multimedia/image_framework/image/pixelmap_native.h>
+#include <QAtomicPointer>
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(ohDnd, "qt.openharmony.dnd")
+
+
+QOhDropTarget::QOhDropTarget(QOhWindowNode *node)
+ : m_node(node)
+{
+
+}
+
+QOhDropTarget::~QOhDropTarget()
+{
+
+}
+
+void QOhDropTarget::handleDragDropEvent(ArkUI_NodeEvent* event)
+{
+ ArkUI_NodeEventType type = OH_ArkUI_NodeEvent_GetEventType(event);
+ switch (type) {
+ case NODE_ON_DRAG_ENTER:
+ handleDragEnter(event);
+ break;
+ case NODE_ON_DRAG_MOVE:
+ handleDrag(event);
+ break;
+ case NODE_ON_DRAG_LEAVE:
+ {
+ QtOh::runOnQtMainThread([this]{
+ if (!QOhWindowNode::nodeDestroyed(m_node)) {
+ auto pw = m_node->platformWindow();
+ if (pw == nullptr)
+ return;
+ QWindowSystemInterface::handleDrag(pw->window(), nullptr,
+ QPoint(), Qt::IgnoreAction,
+ Qt::NoButton, Qt::NoModifier);
+ }
+ });
+ }
+ break;
+ case NODE_ON_DROP:
+ handleDrop(event);
+ break;
+ default:
+ break;
+ }
+}
+
+QMimeData *QOhDropTarget::dropData()
+{
+ QOhDrag *drag = static_cast<QOhDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
+ if (drag && drag->dropData())
+ return drag->dropData();
+ return nullptr;
+}
+
+QWindow *QOhDropTarget::window() const
+{
+ auto pw = m_node->platformWindow();
+ if (pw == nullptr)
+ return nullptr;
+ return pw->window();
+}
+
+void QOhDropTarget::handleDrag(ArkUI_NodeEvent* event)
+{
+ ArkUI_DragEvent *drag = OH_ArkUI_NodeEvent_GetDragEvent(event);
+ if (nullptr == drag)
+ return;
+
+ QOhDragEvent dragEvent(drag);
+ auto actions = dragEvent.actions();
+ auto nodePoint = dragEvent.touchPoint();
+ auto keys = dragEvent.keyboardModifiers(); /* Qt 6 only modify */
+
+ QPlatformDragQtResponse dragResponse(false, Qt::IgnoreAction, QRect());
+ QPointer<QWindow> guard = window();
+ QSharedPointer<QEventLoop> loop(new QEventLoop);
+ /* Qt 6 only modify */
+ QtOh::runOnQtMainThread([this, loop, actions, nodePoint, guard, keys, &dragResponse]{
+ if (!QOhWindowNode::nodeDestroyed(m_node) && !guard.isNull())
+ dragResponse = QWindowSystemInterface::handleDrag(guard.data(), dropData(),
+ QHighDpi::fromNativeLocalPosition(nodePoint, guard.data()),
+ actions, Qt::NoButton, keys);
+
+ if (!loop.isNull() && loop->isRunning())
+ QMetaObject::invokeMethod(loop.data(), "quit", Qt::QueuedConnection);
+ });
+ if (QtOh::wait(loop.data(), 500)) {
+ // timeout
+ dragEvent.failed();
+ } else {
+ dragEvent.setSuggestedDropOperation(dragResponse.acceptedAction());
+ dragEvent.setResult(dragResponse.isAccepted());
+ }
+}
+
+void QOhDropTarget::handleDrop(ArkUI_NodeEvent* event)
+{
+ ArkUI_DragEvent *drag = OH_ArkUI_NodeEvent_GetDragEvent(event);
+ if (drag == nullptr) {
+ return;
+ }
+ QOhDragEvent dragEvent(drag);
+ auto actions = dragEvent.actions();
+ auto data = dropData();
+ data->clear();
+ dragEvent.acquireDatas(data);
+ auto nodePoint = dragEvent.touchPoint();
+ auto keys = dragEvent.keyboardModifiers(); /* Qt 6 only modify */
+
+ QPlatformDropQtResponse dropResponse(false, Qt::IgnoreAction);
+ QPointer<QWindow> guard = window();
+ QSharedPointer<QEventLoop> loop(new QEventLoop);
+ /* Qt 6 only modify */
+ QtOh::runOnQtMainThread([this, data, nodePoint, actions, guard, loop, keys, &dropResponse]{
+ if (!QOhWindowNode::nodeDestroyed(m_node) && !guard.isNull())
+ dropResponse = QWindowSystemInterface::handleDrop(guard.data(), data,
+ QHighDpi::fromNativeLocalPosition(nodePoint, guard.data()),
+ actions, Qt::NoButton, keys);
+ if (!loop.isNull() && loop->isRunning())
+ QMetaObject::invokeMethod(loop.data(), "quit", Qt::QueuedConnection);
+ });
+
+ if (QtOh::wait(loop.data(), 500)) {
+ dragEvent.failed();
+ } else {
+ dragEvent.setResult(dropResponse.isAccepted());
+ }
+}
+
+void QOhDropTarget::handleDragEnter(ArkUI_NodeEvent *event)
+{
+ ArkUI_DragEvent *drag = OH_ArkUI_NodeEvent_GetDragEvent(event);
+ if (nullptr == drag)
+ return;
+
+ QOhDragEvent dragEvent(drag);
+
+ auto types = dragEvent.types();
+ auto actions = dragEvent.actions();
+ auto nodePoint = dragEvent.touchPoint();
+ auto keys = dragEvent.keyboardModifiers(); /* Qt 6 only modify */
+
+ QPlatformDragQtResponse dragResponse(false, Qt::IgnoreAction, QRect());
+ QPointer<QWindow> guard = window();
+ QSharedPointer<QEventLoop> loop(new QEventLoop);
+ /* Qt 6 only modify */
+ QtOh::runOnQtMainThread([this, actions, nodePoint,
+ &dragResponse, types, guard, loop, keys]{
+ if (!QOhWindowNode::nodeDestroyed(m_node) && !guard.isNull()) {
+ auto data = dropData();
+ data->clear();
+ for (int i = 0; i < types.count(); ++i) {
+ data->setData(types.at(i), QByteArray());
+ }
+ dragResponse = QWindowSystemInterface::handleDrag(guard.data(), data,
+ QHighDpi::fromNativeLocalPosition(nodePoint, guard.data()),
+ actions, Qt::NoButton, keys);
+ }
+ if (!loop.isNull() && loop->isRunning())
+ QMetaObject::invokeMethod(loop.data(), "quit", Qt::QueuedConnection);
+ });
+
+
+ if (QtOh::wait(loop.data(), 500)) {
+ dragEvent.failed();
+ } else {
+ dragEvent.setSuggestedDropOperation(dragResponse.acceptedAction());
+ dragEvent.setResult(dragResponse.isAccepted());
+ }
+}
+
+class QOhDragHandler
+{
+public:
+ QAtomicInteger<bool> m_ended;
+ QOhDragHandler(ArkUI_DragAction *dragAction);
+ ~QOhDragHandler();
+ bool setData(QMimeData *dropData);
+ bool createPreview(QDrag *drag);
+ Qt::DropAction startDrag();
+ void setDropResult(Qt::DropAction action);
+ void setSupportedActions(Qt::DropActions actions);
+ static void dragStatusListener(ArkUI_DragAndDropInfo* dragAndDropInfo, void* userData);
+ Qt::DropActions supportedActions() const;
+
+private:
+ ArkUI_DragAction *m_dragAction;
+ ArkUI_DragPreviewOption *m_previewOption;
+ Qt::DropAction m_action;
+ Qt::DropActions m_supportedActions;
+ QEventLoop m_loop;
+ std::unique_ptr<OH_PixelmapNative, decltype(&::OH_PixelmapNative_Release)> m_preview;
+};
+
+QOhDragHandler::QOhDragHandler(ArkUI_DragAction *dragAction)
+ : m_dragAction(dragAction)
+ , m_preview(nullptr, ::OH_PixelmapNative_Release)
+ , m_previewOption(nullptr)
+ , m_action(Qt::IgnoreAction)
+{
+ m_ended.storeRelease(false);
+}
+
+QOhDragHandler::~QOhDragHandler()
+{
+ if (m_dragAction != nullptr) {
+ OH_ArkUI_DragAction_UnregisterStatusListener(m_dragAction);
+ OH_ArkUI_DragAction_Dispose(m_dragAction);
+ }
+
+ if (m_previewOption != nullptr) {
+ OH_ArkUI_DragPreviewOption_Dispose(m_previewOption);
+ }
+}
+
+bool QOhDragHandler::setData(QMimeData *dropData)
+{
+ if (m_dragAction == nullptr)
+ return false;
+ OH_UdmfData *data = QtOh::UdmfHelper::makeUdmfDataFromMime(dropData, false);
+ bool result = OH_ArkUI_DragAction_SetData(m_dragAction, data) == ARKUI_ERROR_CODE_NO_ERROR;
+ if (!result)
+ OH_UdmfData_Destroy(data);
+ return result;
+}
+
+Qt::DropAction QOhDragHandler::startDrag()
+{
+ if (m_dragAction == nullptr)
+ return m_action;
+
+ OH_ArkUI_DragAction_RegisterStatusListener(m_dragAction, this, &QOhDragHandler::dragStatusListener);
+ if (OH_ArkUI_StartDrag(m_dragAction) != ARKUI_ERROR_CODE_NO_ERROR)
+ return m_action;
+
+ if (!m_ended.loadAcquire())
+ m_loop.exec();
+
+ return m_action;
+}
+
+void QOhDragHandler::setDropResult(Qt::DropAction action)
+{
+ m_action = action;
+ if (m_loop.isRunning())
+ m_loop.quit();
+
+ m_ended.storeRelease(true);
+}
+
+void QOhDragHandler::setSupportedActions(Qt::DropActions actions)
+{
+ m_supportedActions = actions;
+}
+
+void QOhDragHandler::dragStatusListener(ArkUI_DragAndDropInfo *dragAndDropInfo, void *userData)
+{
+ ArkUI_DragStatus status = OH_ArkUI_DragAndDropInfo_GetDragStatus(dragAndDropInfo);
+ QOhDragHandler *handler = reinterpret_cast<QOhDragHandler *>(userData);
+ Q_ASSERT(handler != nullptr);
+ if (status == ARKUI_DRAG_STATUS_ENDED) {
+ ArkUI_DragEvent *dragEvent = OH_ArkUI_DragAndDropInfo_GetDragEvent(dragAndDropInfo);
+ Qt::DropAction action = Qt::IgnoreAction;
+ if (dragEvent != nullptr) {
+ ArkUI_DragResult result = ARKUI_DRAG_RESULT_CANCELED;
+ OH_ArkUI_DragEvent_GetDragResult(dragEvent, &result);
+ ArkUI_DropOperation operation = ARKUI_DROP_OPERATION_COPY;
+ OH_ArkUI_DragEvent_GetDropOperation(dragEvent, &operation);
+ switch (result) {
+ case ARKUI_DRAG_RESULT_SUCCESSFUL:
+ action = (operation == ARKUI_DROP_OPERATION_COPY) ? Qt::CopyAction : Qt::MoveAction;
+ break;
+ default:
+ break;
+ }
+ }
+
+ handler->setDropResult(action);
+ } else if (status == ARKUI_DRAG_STATUS_STARTED) {
+ ArkUI_DragEvent *dragEvent = OH_ArkUI_DragAndDropInfo_GetDragEvent(dragAndDropInfo);
+ if (dragEvent != nullptr) {
+ ArkUI_DropOperation operation = ARKUI_DROP_OPERATION_COPY;
+ const Qt::DropActions possibleActions = handler->supportedActions();
+ if (possibleActions & Qt::MoveAction)
+ operation = ARKUI_DROP_OPERATION_MOVE;
+ OH_ArkUI_DragEvent_SetSuggestedDropOperation(dragEvent, operation);
+ }
+ }
+}
+
+Qt::DropActions QOhDragHandler::supportedActions() const
+{
+ return m_supportedActions;
+}
+
+#define CHECK_RESULT(result, msg, action) \
+if (!(result)) { \
+ qCWarning(ohDnd) << msg; \
+ action; \
+}
+
+bool QOhDragHandler::createPreview(QDrag *drag)
+{
+ const QPixmap pixmap = drag->pixmap();
+ const bool hasPixmap = !pixmap.isNull();
+ const QPoint hotSpot = drag->hotSpot();
+ OH_ArkUI_DragAction_SetTouchPointX(m_dragAction, qMax(hotSpot.x(), 0));
+ OH_ArkUI_DragAction_SetTouchPointY(m_dragAction, qMax(hotSpot.y(), 0));
+
+ if (hasPixmap) {
+ QImage image = pixmap.toImage();
+ m_preview = std::unique_ptr<OH_PixelmapNative, decltype(&::OH_PixelmapNative_Release)>
+ (QtOh::UdmfHelper::createNativePixelMapFromQImage(image),
+ ::OH_PixelmapNative_Release);
+
+ OH_PixelmapNative *pixelmapArray[1]{ m_preview.get() };
+ int32_t result = OH_ArkUI_DragAction_SetPixelMaps(m_dragAction, pixelmapArray, 1);
+
+ CHECK_RESULT(result == ARKUI_ERROR_CODE_NO_ERROR, "create native pixmap failed.", return false);
+ }
+ m_previewOption = OH_ArkUI_CreateDragPreviewOption();
+ bool enabled = QtOh::isEnvironmentVariableIsTrue("QT_ENABLE_DRAG_NUMBADGE", true);
+ // qWarning() << "drag num badge enabled:" << enabled;
+ OH_ArkUI_DragPreviewOption_SetNumberBadgeEnabled(m_previewOption, enabled);
+ OH_ArkUI_DragAction_SetDragPreviewOption(m_dragAction, m_previewOption);
+ return true;
+}
+
+QOhDrag::QOhDrag() = default;
+QOhDrag::~QOhDrag()
+{
+
+}
+
+Qt::DropAction QOhDrag::drag(QDrag *drag)
+{
+ QMimeData *dropData = drag->mimeData();
+ Qt::DropAction dragResult = Qt::IgnoreAction;
+
+ QWindow *focusWindow = QGuiApplication::focusWindow();
+ if (focusWindow == nullptr)
+ return dragResult;
+
+ WindowNode *node = reinterpret_cast<WindowNode *>(focusWindow->winId());
+ CHECK_RESULT(node != nullptr, "get window node failed.", return dragResult);
+
+ ArkUI_DragAction *dragAction = OH_ArkUI_CreateDragActionWithNode(node->node);
+ CHECK_RESULT(dragAction != nullptr, "create drag action failed.", return dragResult);
+ QScopedPointer<QOhDragHandler> dragHandler(new QOhDragHandler(dragAction));
+
+ bool result = dragHandler->setData(dropData);
+ CHECK_RESULT(result, "set drag data failed.", return dragResult);
+ result = dragHandler->createPreview(drag);
+ CHECK_RESULT(result, "create drag preview failed.", return dragResult);
+ dragHandler->setSupportedActions(drag->supportedActions());
+ dragResult = dragHandler->startDrag();
+ return dragResult;
+}
+
+QMimeData *QOhDrag::dropData()
+{
+ if (const QDrag *drag = currentDrag())
+ return drag->mimeData();
+ return &m_mimeData;
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,51 @@
+#ifndef QOHDRAG_H
+#define QOHDRAG_H
+
+#include <QtCore/qhash.h>
+#include <QtGui/qdrag.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qmimedata.h>
+#include <qpa/qplatformdrag.h>
+
+struct ArkUI_NodeEvent;
+struct ArkUI_DragAction;
+struct OH_PixelmapNative;
+
+QT_BEGIN_NAMESPACE
+class QOhWindowNode;
+class QMimeData;
+class QOhDropTarget
+{
+public:
+ QOhDropTarget(QOhWindowNode *node);
+ ~QOhDropTarget();
+ void handleDrag(ArkUI_NodeEvent* event);
+ void handleDrop(ArkUI_NodeEvent* event);
+ void handleDragEnter(ArkUI_NodeEvent* event);
+ void handleDragDropEvent(ArkUI_NodeEvent* event);
+ QMimeData *dropData();
+private:
+ QWindow *window() const;
+private:
+ QOhWindowNode *m_node;
+};
+
+class QOhDrag : public QPlatformDrag
+{
+public:
+ QOhDrag();
+ virtual ~QOhDrag();
+
+ void cancelDrag() override { QOhDrag::m_canceled = true; }
+ Qt::DropAction drag(QDrag *drag) override;
+
+ QMimeData *dropData();
+
+private:
+ QMimeData m_mimeData;
+ static inline bool m_canceled = true;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHDRAG_H
new file mode 100644
@@ -0,0 +1,182 @@
+#ifndef QOHEVENT_H
+#define QOHEVENT_H
+
+#include <variant>
+#include <qrect.h>
+#include <qwindow.h>
+#include <qpointer.h>
+#include <QtGui/qpointingdevice.h> /* Qt 6 only modify */
+#include <qpa/qwindowsysteminterface.h>
+#include <ace/xcomponent/native_xcomponent_key_event.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+QT_BEGIN_NAMESPACE
+namespace QtOh {
+
+enum EventType {
+ Key,
+ Wheel,
+ Touch,
+ Mouse,
+ Tablet,
+ Gesture,
+ EnterLeave,
+ GeometryChange
+};
+
+class Event
+{
+public:
+ Event(int _type)
+ : type(_type)
+ , timestamp(0)
+ , window(nullptr)
+ {
+
+ }
+ virtual ~Event() {}
+ int type;
+ int64_t timestamp;
+ QPointer<QWindow> window;
+};
+
+class InputEvent : public Event
+{
+public:
+ InputEvent(int _type) : Event(_type) {}
+ int64_t deviceId;
+ Qt::KeyboardModifiers keyboardModifiers;
+};
+
+class KeyEvent : public InputEvent
+{
+public:
+ KeyEvent()
+ : InputEvent(Key) {}
+ OH_NativeXComponent_KeyCode code;
+ OH_NativeXComponent_KeyAction action;
+ OH_NativeXComponent_EventSourceType stype;
+ int64_t timeStamp;
+};
+
+#if OHOS_SDK_VERSION >= 17
+class NewKeyEvent : public InputEvent
+{
+public:
+ NewKeyEvent()
+ : InputEvent(Key) {}
+ int32_t type;
+ int32_t code;
+ QString text;
+};
+#endif
+
+class WheelEvent : public InputEvent
+{
+public:
+ WheelEvent()
+ : InputEvent(Wheel)
+ , local()
+ , global()
+ , toolType(0)
+ , axisAction(0)
+ , scrollStep(1)
+ , sourceType(0)
+ , pinchScale(0.f)
+ , verticalValue(0.f)
+ , horizontalValue(0.f)
+ {
+
+ }
+
+ QPointF local;
+ QPointF global;
+ int32_t toolType;
+ int32_t axisAction; /* since api 15 */
+ int32_t scrollStep; /* since api 17 */
+ int32_t sourceType;
+ float pinchScale;
+ double verticalValue;
+ double horizontalValue;
+};
+
+class TouchEvent : public InputEvent
+{
+public:
+// struct TouchPoint {
+// int id;
+// QPointF pos;
+// double size;
+// float force;
+// QPointF screen;
+// QPointF global;
+// int pointState;
+// bool isPressed;
+// long long timeStamp;
+// };
+ TouchEvent() : InputEvent(Touch)
+ , actionType(-1)
+ {
+
+ }
+ int id;
+ int actionType;
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+ /* Qt 6 only modify */
+ QPointingDevice::DeviceType deviceType;
+ QPointingDevice::PointerType pointerType;
+};
+
+class TabletEvent : public InputEvent
+{
+public:
+ TabletEvent():InputEvent(Tablet){}
+ qreal presure;
+ QPointF global;
+ qint64 uniqueID;
+ int xTilt, yTilt;
+ double rotation;
+ Qt::MouseButton btn;
+ QPointingDevice::PointerType pointerType; /* Qt 6 only modify */
+};
+
+class GestureEvent : public InputEvent
+{
+public:
+ GestureEvent() : InputEvent(Gesture){}
+
+ qreal value;
+ QPointF local;
+ QPointF global;
+ int32_t sourceType;
+ Qt::NativeGestureType gestureType;
+};
+
+class GeometryChangeEvent : public Event
+{
+public:
+ GeometryChangeEvent() : Event(GeometryChange) {}
+ QRect rect;
+ int reason;
+};
+
+class EnterLeaveEvent : public Event
+{
+public:
+ EnterLeaveEvent() : Event(EnterLeave) {}
+ bool enter;
+};
+
+class MouseEvent : public InputEvent
+{
+public:
+ MouseEvent() : InputEvent(Mouse) {}
+ QPointF xcPos;
+ QPointF scPos; /* screenPos */
+ QEvent::Type mouseEventType;
+ Qt::MouseButton mouseButton;
+};
+
+}
+Q_DECLARE_METATYPE(QtOh::KeyEvent)
+QT_END_NAMESPACE
+#endif // QOHEVENT_H
new file mode 100644
@@ -0,0 +1,1026 @@
+#include <cmath>
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef QT_NO_EVENTFD
+# include <sys/eventfd.h>
+#endif
+
+#include <QDebug>
+#include <QGuiApplication>
+#include <QtCore/QWriteLocker> /* Qt 6 only modify */
+#include <qopenharmonydefines.h>
+#include <qpa/qplatformcursor.h>
+#include <private/qhighdpiscaling_p.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtGui/private/qpointingdevice_p.h> /* Qt 6 only modify */
+#include "qohkeys.h"
+#include "qohevent.h"
+#include "qopenharmony.h"
+#include "qohplatformtheme.h"
+#include "qohwindowcontext.h"
+#include "qoheventdispatcher.h"
+#include "qohplatformintegration.h"
+#include "qohplatforminputcontext.h"
+#include "qohplatformopenglwindow.h"
+
+#if OHOS_SDK_VERSION >= 17
+#include <arkui/native_key_event.h>
+#endif
+/* Qt 6 only modify */
+using namespace Qt::Literals::StringLiterals;
+
+class QOhPointingDevice : public QPointingDevice
+{
+public:
+ QOhPointingDevice(const QString &name, qint64 systemId, QInputDevice::DeviceType devType,
+ PointerType pType, Capabilities caps, int maxPoints, int buttonCount,
+ const QString &seatName = QString(),
+ QPointingDeviceUniqueId uniqueId = QPointingDeviceUniqueId(),
+ QObject *parent = nullptr)
+ : QPointingDevice(name, systemId, devType,
+ pType, caps, maxPoints, buttonCount,
+ seatName,
+ uniqueId,
+ parent)
+ , m_systemId(systemId)
+ {
+
+ }
+ qint64 systemId() const
+ {
+ return m_systemId;
+ }
+
+private:
+ qint64 m_systemId;
+};
+
+QPoint translatePoint(const QPointF &global, QWindow *w)
+{
+ auto pw = w->handle();
+ QPoint local = pw->mapFromGlobal(global.toPoint());
+ return local;
+}
+
+using WheelEvents = QMultiHash<QtOh::EventType, QSharedPointer<QtOh::WheelEvent>>;
+class QOhEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
+{
+ Q_DECLARE_PUBLIC(QOhEventDispatcher)
+ Q_DISABLE_COPY_MOVE(QOhEventDispatcherPrivate)
+
+public:
+ QOhEventDispatcherPrivate(QOhPlatformIntegration * const inte);
+ ~QOhEventDispatcherPrivate();
+
+ void operator()(QSharedPointer<QtOh::WheelEvent> e);
+ void operator()(QSharedPointer<QtOh::TouchEvent> e);
+ void operator()(QSharedPointer<QtOh::MouseEvent> e);
+ void operator()(QSharedPointer<QtOh::TabletEvent> e);
+ void operator()(QSharedPointer<QtOh::GestureEvent> e);
+ void operator()(QSharedPointer<QtOh::EnterLeaveEvent> e);
+ void operator()(QSharedPointer<QtOh::GeometryChangeEvent> e);
+ void operator()(QSharedPointer<QtOh::KeyEvent> e);
+#if OHOS_SDK_VERSION >= 17
+ void operator()(QSharedPointer<QtOh::NewKeyEvent> e);
+#endif
+ void handleKeyEvent(QWindow *window, QEvent::Type type, int32_t code, Qt::KeyboardModifiers mod, bool autorep = false);
+#if OHOS_SDK_VERSION < 18
+ void handleWindowHidden(const QWindow *window);
+#endif
+
+protected:
+ void uniqueGeometryChangeEvent();
+ void wakeUpWheelEvent(QSharedPointer<QtOh::WheelEvent> event);
+ void wakeUpWheelPinch(QSharedPointer<QtOh::WheelEvent> event);
+ void reduceWheelEvent(QSharedPointer<QtOh::WheelEvent> event);
+ void reduceWheelEventImple(QSharedPointer<QtOh::WheelEvent> event);
+ void mouseMove(QSharedPointer<QtOh::MouseEvent> event);
+ void mousePress(QSharedPointer<QtOh::MouseEvent> event);
+ void mouseRelease(QSharedPointer<QtOh::MouseEvent> event);
+
+ void nonClientAreaMouseMove(QSharedPointer<QtOh::MouseEvent> event);
+ void nonClientAreaMousePress(QSharedPointer<QtOh::MouseEvent> event);
+ void nonClientAreaMouseRelease(QSharedPointer<QtOh::MouseEvent> event);
+
+ QPointF getLastTouchPoint(int id) const;
+ void updateLastTouchPoints(int id, const QPointF &point);
+
+ void updateCursorPos(const QPoint &pt);
+private:
+ void removeWheelEvent(QSharedPointer<QtOh::WheelEvent> event);
+ void insertWheelEvent(QSharedPointer<QtOh::WheelEvent> event);
+ void replaceWheelEvent(QSharedPointer<QtOh::WheelEvent> event);
+
+ QSharedPointer<QtOh::WheelEvent> getLastWheelEvent() {
+ QReadLocker locker(&m_lock);
+ auto event = m_WheelEvents.value(QtOh::Wheel, QSharedPointer<QtOh::WheelEvent>());
+ return event;
+ }
+ /* Qt 6 only modify */
+ QSharedPointer<QOhPointingDevice> touchDevice(qint64 systemId, QPointingDevice::DeviceType, QPointingDevice::PointerType pointerType);
+#if QT_CONFIG(tabletevent)
+ QSharedPointer<QPointingDevice> tabletDevice(QPointingDevice::PointerType pointerType) const
+ {
+ for (const auto &d : m_tabletDevices) {
+ if (d->pointerType() == pointerType)
+ return d;
+ }
+ return {};
+ }
+#endif
+private:
+ using Devices = QList<QSharedPointer<QOhPointingDevice>>;
+ Devices m_devices;
+ QList<QSharedPointer<QPointingDevice>> m_tabletDevices;
+
+ int m_timerId;
+ QReadWriteLock m_lock;
+ WheelEvents m_WheelEvents;
+ QHash<int, QPointF> m_lastTouchPoints;
+ QOhPlatformIntegration *const m_inte;
+ static QAtomicPointer<QOhEventDispatcher> m_qinstance;
+ QHash<QWindow*, QSharedPointer<QtOh::GeometryChangeEvent>> m_geometryEvents;
+#if OHOS_SDK_VERSION < 18
+ QSharedPointer<QtOh::MouseEvent> m_button_down;
+#endif
+};
+QAtomicPointer<QOhEventDispatcher> QOhEventDispatcherPrivate::m_qinstance = nullptr;
+
+QOhEventDispatcherPrivate::QOhEventDispatcherPrivate(QOhPlatformIntegration * const inte)
+ : QEventDispatcherUNIXPrivate()
+ , m_timerId(0)
+ , m_inte(inte)
+{
+ Q_ASSERT(nullptr != inte);
+}
+
+QOhEventDispatcherPrivate::~QOhEventDispatcherPrivate()
+{
+ {
+ QWriteLocker locker(&m_lock);
+ m_WheelEvents.clear();
+ m_geometryEvents.clear();
+ }
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::WheelEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+
+#if OHOS_SDK_VERSION >= 15
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == e->toolType) {
+ /* 鼠标滚轮操作 */
+ wakeUpWheelEvent(e);
+ return;
+ } else if (UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == e->toolType) {
+ /* 触控板操作 */
+ if (!qFuzzyIsNull(e->pinchScale)) {
+ /* 触控板捏合处理 */
+ wakeUpWheelPinch(e);
+ } else {
+#endif
+ reduceWheelEvent(e);
+#if OHOS_SDK_VERSION >= 15
+ }
+ }
+#endif
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::TouchEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+
+ /* Qt 6 only modify */
+ auto dev = touchDevice(e->deviceId, e->deviceType, e->pointerType);
+ QWindow *w = e->window.data();
+ if (UI_TOUCH_EVENT_ACTION_CANCEL == e->actionType) {
+ QWindowSystemInterface::handleTouchCancelEvent(w, dev.data(),
+#if OHOS_SDK_VERSION >= 17
+ e->keyboardModifiers
+#else
+ QOhKeys::keyboardModifiers()
+#endif
+ );
+ updateCursorPos(QPoint());
+ return;
+ }
+
+ QOhPlatformInputContext::ohInputContext()->setRequestKeyboardReason(InputMethod::RequestKeyboardReason::TOUCH);
+
+ /* Qt 6 only modify std::as_const */
+ for (const auto &point : std::as_const(e->touchPoints))
+ {
+ updateCursorPos(point.rawPositions.first().toPoint());
+ }
+
+ /* NOTE 鸿蒙触摸事件源
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN = 0
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHSCREEN
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHPAD,
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_JOYSTICK,
+ * OH_NATIVEXCOMPONENT_SOURCE_TYPE_KEYBOARD
+ */
+#if OHOS_SDK_VERSION >= 17
+ Qt::KeyboardModifiers modify = e->keyboardModifiers;
+#else
+ Qt::KeyboardModifiers modify = QOhKeys::keyboardModifiers();
+#endif
+ if (UI_TOUCH_EVENT_ACTION_UP == e->actionType) {
+ /* NOTE 有popup窗口时press事件可能会触发qt额外模拟的press事件
+ * 此时应将系统release事件处理放到模拟的press事件之后,如果直接在ui线程中
+ * 将release事件加入队列会导致release事件直接执行,在模拟的press事件之前
+ */
+ QtOh::runOnQtMainThread([=]{
+ QWindowSystemInterface::handleTouchEvent(w, dev.data(), e->touchPoints, modify); /* Qt 6 only modify */
+ });
+ } else {
+ QWindowSystemInterface::handleTouchEvent(w, dev.data(), e->touchPoints, modify); /* Qt 6 only modify */
+ }
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::MouseEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+
+ QPoint p = e->scPos.toPoint();
+ updateCursorPos(p);
+ QOhPlatformInputContext::ohInputContext()->setRequestKeyboardReason(InputMethod::RequestKeyboardReason::MOUSE);
+
+ if (e->mouseEventType == QEvent::MouseMove) {
+ mouseMove(e);
+ } else if (e->mouseEventType == QEvent::MouseButtonPress) {
+ mousePress(e);
+ } else if (e->mouseEventType == QEvent::MouseButtonRelease) {
+ /* NOTE 有popup窗口时press事件可能会触发qt额外模拟的press事件
+ *此时应将系统release事件处理放到模拟的press事件之后,如果直接在ui线程中
+ *将release事件加入队列会导致release事件直接执行,在模拟的press事件之前
+ */
+ QtOh::runOnQtMainThread([=, this]{
+ mouseRelease(e);
+ });
+ } else if (e->mouseEventType == QEvent::NonClientAreaMouseMove) {
+ nonClientAreaMouseMove(e);
+ } else if (e->mouseEventType == QEvent::NonClientAreaMouseButtonPress) {
+ nonClientAreaMousePress(e);
+ } else if (e->mouseEventType == QEvent::NonClientAreaMouseButtonRelease) {
+ nonClientAreaMouseRelease(e);
+ }
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::TabletEvent> e)
+{
+/* Qt 6 only modify */
+#if QT_CONFIG(tabletevent)
+ if (e.isNull() || e->window.isNull())
+ return;
+
+ updateCursorPos(e->global.toPoint());
+ auto device = tabletDevice(e->pointerType);
+ if (device.isNull()) {
+ QInputDevice::Capabilities caps(QInputDevice::Capability::Position
+ | QInputDevice::Capability::MouseEmulation
+ | QInputDevice::Capability::Hover);
+ caps |= QInputDevice::Capability::Pressure;
+ caps |= QInputDevice::Capability::Rotation;
+ caps |= QInputDevice::Capability::XTilt;
+ caps |= QInputDevice::Capability::YTilt;
+ const qint64 uniqueId = e->deviceId | (qint64(e->pointerType) << 32L);
+ device.reset(new QPointingDevice("ohostablet"_L1,
+ e->deviceId, QInputDevice::DeviceType::Stylus,
+ e->pointerType, caps, 1, 3, QString(),
+ QPointingDeviceUniqueId::fromNumericId(uniqueId)));
+ QWindowSystemInterface::registerInputDevice(device.data());
+ m_tabletDevices.append(device);
+ }
+
+ QWindowSystemInterface::handleTabletEvent(e->window, NSTOMS(e->timestamp), device.data(), translatePoint(e->global, e->window), e->global,
+ e->btn, e->presure,
+ e->xTilt, e->yTilt, 0, e->rotation, 0,
+#if OHOS_SDK_VERSION >= 17
+ e->keyboardModifiers
+#else
+ QOhKeys::keyboardModifiers()
+#endif
+ );
+#endif
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::GestureEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+
+ updateCursorPos(QPoint(e->global.x(), e->global.y()));
+
+ /* Qt 6 only modify */
+ auto dev = touchDevice(e->deviceId, QPointingDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger);
+
+ /* NOTE 鸿蒙手势事件源
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ QPointF local = QHighDpi::fromNativeLocalPosition(e->local, e->window.data());
+ QPointF global = QHighDpi::fromNativePixels(e->global, e->window.data());
+ QWindowSystemInterface::handleGestureEventWithRealValue(e->window, NSTOMS(e->timestamp), dev.data(),
+ e->gestureType, e->value, local, global);
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::EnterLeaveEvent> e)
+{
+ if (!e.isNull() && !e->window.isNull() && e->window->handle()) {
+ if (e->enter) {
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ if ((tlw && tlw != e->window) || QOhWindowContext::underMouseWindow() == e->window)
+ return;
+
+ QPoint globalPos, localPos;
+ if (QPlatformScreen *ps = e->window->screen()->handle()) {
+ if (ps->cursor()) {
+ globalPos = ps->cursor()->pos();
+ localPos = e->window->handle()->mapFromGlobal(globalPos);
+ }
+ }
+
+ QWindowSystemInterface::handleEnterEvent(e->window, localPos, globalPos);
+ QOhWindowContext::setUnderMouseWindow(e->window);
+ } else {
+ if (QOhWindowContext::underMouseWindow() == e->window) {
+ QOhWindowContext::setUnderMouseWindow(nullptr);
+ QWindowSystemInterface::handleLeaveEvent(e->window);
+ }
+ }
+ }
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::GeometryChangeEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+ {
+ QWriteLocker locker(&m_lock);
+ m_geometryEvents.insert(e->window, e);
+ }
+}
+
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::KeyEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+ QWindow *w = e->window.data();
+ QWindow *keyGrabber = QOhWindowContext::keyGrabberWindow();
+ QWindow *receiver = keyGrabber ? keyGrabber : w;
+ OH_NativeXComponent_KeyCode code = e->code;
+ OH_NativeXComponent_KeyAction action = e->action;
+ OH_NativeXComponent_EventSourceType stype = e->stype;
+ Q_UNUSED(stype);
+
+ QEvent::Type t = (action == OH_NATIVEXCOMPONENT_KEY_ACTION_DOWN) ? QEvent::KeyPress :
+ ((action == OH_NATIVEXCOMPONENT_KEY_ACTION_UP) ? QEvent::KeyRelease : QEvent::None);
+
+ /* 长按判断
+ * 记录按下的字符,判断字符是否已经按下
+ * 如果字符已经按下则认为是长按状态
+ */
+ bool ar = QOhKeys::autoRepeat(action, code);
+
+ handleKeyEvent(receiver, t, code, QOhKeys::keyboardModifiers(), ar);
+}
+
+void QOhEventDispatcherPrivate::handleKeyEvent(QWindow *window, QEvent::Type type, int32_t code, Qt::KeyboardModifiers mod, bool autorep)
+{
+ if (autorep && type == QEvent::KeyPress) {
+ /* 不重复发送按下的辅助按键 */
+ if (code == KEY_CTRL_LEFT || code == KEY_CTRL_RIGHT
+ || code == KEY_SHIFT_LEFT || code == KEY_SHIFT_RIGHT
+ || code == KEY_ALT_LEFT || code == KEY_ALT_RIGHT)
+ return;
+ }
+
+ auto nativeModifier = mod;
+ int state = 0;
+ state |= (nativeModifier & Qt::ShiftModifier ? int(Qt::ShiftModifier) : 0);
+ state |= (nativeModifier & Qt::ControlModifier ? int(Qt::ControlModifier) : 0);
+ state |= (nativeModifier & Qt::AltModifier ? int(Qt::AltModifier) : 0);
+ state |= (nativeModifier & Qt::MetaModifier ? int(Qt::MetaModifier) : 0);
+
+ if (code == KEY_CTRL_LEFT || code == KEY_CTRL_RIGHT)
+ state = state ^ Qt::ControlModifier;
+ else if (code == KEY_SHIFT_LEFT || code == KEY_SHIFT_RIGHT)
+ state = state ^ Qt::ShiftModifier;
+ else if (code == KEY_ALT_LEFT || code == KEY_ALT_RIGHT)
+ state = state ^ Qt::AltModifier;
+
+ Qt::KeyboardModifiers modifiers(state);
+ if (QOhKeys::isKeypad(code))
+ modifiers |= Qt::KeypadModifier;
+
+ Qt::Key key = QOhKeys::ohCode2QtKey(code, modifiers);
+
+ /* Map SHIFT + Tab to SHIFT + BackTab */
+ if (key == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
+ key = Qt::Key_Backtab;
+
+ /* 赋值键盘文本 */
+ QString keyText = QOhKeys::key2String(key, modifiers);
+ QWindowSystemInterface::handleExtendedKeyEvent(window, type, key, modifiers, code, code, nativeModifier, keyText, autorep);
+}
+
+#if OHOS_SDK_VERSION < 18
+/* 处理mousePress事件时关闭窗口系统不会发送mouseRelease事件,在此处补发 */
+void QOhEventDispatcherPrivate::handleWindowHidden(const QWindow *window)
+{
+ QtOh::runOnJsUIThreadNoWait([this, window]{
+ if (m_button_down.isNull() || window != m_button_down->window)
+ return;
+
+ m_button_down->mouseEventType = QEvent::MouseButtonRelease;
+ mouseRelease(m_button_down);
+ });
+}
+#endif
+
+void QOhEventDispatcherPrivate::uniqueGeometryChangeEvent()
+{
+ QHash<QWindow*, QSharedPointer<QtOh::GeometryChangeEvent>> events {};
+ {
+ QWriteLocker locker(&m_lock);
+ events.swap(m_geometryEvents);
+ }
+
+ for (const auto &e : events) {
+ if (e.isNull() || e->window.isNull())
+ continue;
+
+ if (auto *pw = dynamic_cast<QOhPlatformOpenGLWindow *>(e->window->handle())) {
+ pw->setNativeGeometry(e->rect);
+ }
+ QWindowSystemInterface::handleGeometryChange(e->window, e->rect);
+ }
+}
+
+#if OHOS_SDK_VERSION >= 17
+void QOhEventDispatcherPrivate::operator()(QSharedPointer<QtOh::NewKeyEvent> e)
+{
+ if (e.isNull() || e->window.isNull())
+ return;
+ QWindow *w = e->window.data();
+ QWindow *keyGrabber = QOhWindowContext::keyGrabberWindow();
+ QWindow *receiver = keyGrabber ? keyGrabber : w;
+ auto code = e->code;
+
+ QEvent::Type type = QEvent::None;
+ if (e->type == ARKUI_KEY_EVENT_DOWN)
+ type = QEvent::KeyPress;
+ else if (e->type == ARKUI_KEY_EVENT_UP)
+ type = QEvent::KeyRelease;
+
+// bool autoRepeat = e->type == ARKUI_KEY_EVENT_LONG_PRESS;
+ bool autoRepeat = QOhKeys::autoRepeat(type == QEvent::KeyPress ? 0 : 1, code);
+
+ handleKeyEvent(receiver, type, code,
+#if OHOS_SDK_VERSION >= 17
+ e->keyboardModifiers
+#else
+ QOhKeys::keyboardModifiers()
+#endif
+ , autoRepeat);
+}
+#endif
+
+#if OHOS_SDK_VERSION >= 15
+static Qt::ScrollPhase ohAxisActionToQtScrollPhase(int32_t action)
+{
+ static QHash<int32_t, Qt::ScrollPhase> convert {
+ { UI_AXIS_EVENT_ACTION_NONE, Qt::NoScrollPhase },
+ { UI_AXIS_EVENT_ACTION_BEGIN, Qt::ScrollBegin },
+ { UI_AXIS_EVENT_ACTION_UPDATE, Qt::ScrollUpdate },
+ { UI_AXIS_EVENT_ACTION_END, Qt::ScrollEnd },
+ { UI_AXIS_EVENT_ACTION_CANCEL, Qt::NoScrollPhase}
+ };
+ return convert.value(action, Qt::NoScrollPhase);
+}
+#endif
+
+void QOhEventDispatcherPrivate::wakeUpWheelEvent(QSharedPointer<QtOh::WheelEvent> event)
+{
+ if (event.isNull())
+ return;
+ updateCursorPos(event->global.toPoint());
+
+ if (nullptr == event->window ||
+ QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ if (!tlw)
+ tlw = event->window;
+
+ QPlatformScreen *screen = tlw->screen()->handle();
+ QPoint localPos = event->local.toPoint();
+ Qt::KeyboardModifiers mf { Qt::NoModifier };
+#if OHOS_SDK_VERSION >= 15
+ /* 鼠标滚轮回调中 X/Y轴事件中值代表角度
+ * 角度转换为像素值的公式:
+ * px = (axis_value / 15 * 21) * scaleDensity = (7 * axis_value * scaleDensity)/5
+ * 1.axis_value 为多模上报角度
+ * 2.单格高度:21 vp (未加权(权:系统设置中的一次滚几格,此配置已被多模应用,UI框架拿到是加权后的角度))
+ * 3.单格对应角度:15度
+ *
+ * 触控板回调中, X/Y轴事件中值代表像素
+ * 公式通过角度转像素公式反推: axis_value = (px * 5) / (7 * scaleDensity)
+ * 转成Qt中的对应角度:axis_value = (px * 5) / (7 * scaleDensity) * 8
+ * axis_value = (px * 40) / (7 * scaleDensity)
+ */
+ auto [pixelDelta, angleDelta] = [event, screen]()->std::tuple<QPoint, QPoint>{
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == event->toolType) {
+ constexpr int angleXMin = -120;
+ constexpr int angleXMax = 120;
+ int xAxisValueMultiplier = QtOh::apiVersion() >= 17 ? -8 : -10;
+
+ constexpr int angleYMin = angleXMin;
+ constexpr int angleYMax = angleXMax;
+ int yAxisValueMultiplier = xAxisValueMultiplier;
+
+ int step = QtOh::apiVersion() >= 17 ? event->scrollStep : 1;
+ if (QtOh::apiVersion() >= 17)
+ QOhPlatformTheme::wheelScrollLines = step;
+
+ QPoint angle;
+ angle.setY(qBound(angleYMin, static_cast<int>(event->verticalValue / step * yAxisValueMultiplier), angleYMax));
+ angle.setX(qBound(angleXMin, static_cast<int>(event->horizontalValue / step * xAxisValueMultiplier), angleXMax));
+
+ return std::make_tuple(
+ QPoint((angle.x() * 7 * QtOh::densityPixels(screen)) / 5 ,
+ (angle.y() * 7 * QtOh::densityPixels(screen)) / 5),
+
+ angle
+ );
+ } else if (UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == event->toolType) {
+ QPoint pixel(event->horizontalValue, event->verticalValue);
+ /* NOTE 鸿蒙值和Qt相反,乘负数取反 */
+ return std::make_tuple(
+ pixel,
+ QPoint((pixel.x() * -1 * 40.f) / (7.f * QtOh::densityPixels(screen)) / 3,
+ (pixel.y() * -1 * 40.f) / (7.f * QtOh::densityPixels(screen)) / 3)
+ );
+ }
+ return std::make_tuple(QPoint(), QPoint());
+ }();
+#else
+ float pinchValue = event->pinchScale;
+ bool isScale = !qFuzzyIsNull(pinchValue);
+ QPoint pixelDelta;
+ if(isScale) {
+ mf = Qt::ControlModifier;
+ pixelDelta.setY(pinchValue > 1.0f ? -12 : 12);
+ } else {
+ mf = Qt::NoModifier;
+ pixelDelta.setY(event->verticalValue);
+ pixelDelta.setX(event->horizontalValue);
+ }
+
+ constexpr int angleXMin = -120;
+ constexpr int angleXMax = 120;
+ constexpr int xAxisValueMultiplier = -10;
+
+ constexpr int angleYMin = angleXMin;
+ constexpr int angleYMax = angleXMax;
+ constexpr int yAxisValueMultiplier = xAxisValueMultiplier;
+
+ QPoint angleDelta;
+ angleDelta.setY(qBound(angleYMin, static_cast<int>(pixelDelta.y() * yAxisValueMultiplier), angleYMax));
+ angleDelta.setX(qBound(angleXMin, static_cast<int>(pixelDelta.x() * xAxisValueMultiplier), angleXMax));
+#endif
+
+ /* NOTE QML中 Flickable使用的是毫秒级别计算动画效果
+ * 鸿蒙的时间戳为纳秒级别
+ * 1s【秒】 = 1000ms【毫秒】
+ * 1ms【毫秒】 = 1000μs【微秒】
+ * 1μs【微秒】 = 1000ns【纳秒】
+ * 1ns 【纳秒】 = 1000ps【皮秒】
+ */
+ QWindowSystemInterface::handleWheelEvent(tlw, NSTOMS(event->timestamp),
+ localPos, event->global,
+ pixelDelta, angleDelta,
+ mf | event->keyboardModifiers
+#if OHOS_SDK_VERSION >= 15
+ , (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == event->toolType) ? Qt::NoScrollPhase : ohAxisActionToQtScrollPhase(event->axisAction)
+#else
+ , Qt::NoScrollPhase
+#endif
+ );
+}
+/*!
+ * \brief 处理触控板的捏合缩放处理
+ * \param event 触控板轴事件
+ */
+void QOhEventDispatcherPrivate::wakeUpWheelPinch(QSharedPointer<QtOh::WheelEvent> event)
+{
+#if OHOS_SDK_VERSION >= 15
+ if (event.isNull())
+ return;
+ updateCursorPos(event->global.toPoint());
+
+ if (nullptr == event->window ||
+ QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ if (!tlw)
+ tlw = event->window;
+
+ // QWindowSystemInterface::handleGestureEventWithRealValue中没有坐标缩放
+ QPoint globalPos = QHighDpi::fromNativePixels(event->global.toPoint(), tlw);
+ QPoint localPos = tlw->mapFromGlobal(globalPos);
+ // QPoint globalPos = QHighDpi::fromNativeLocalPosition(event->global.toPoint(), tlw);
+ // QPoint localPos = tlw->mapFromGlobal(globalPos);
+
+ /* NOTE 鸿蒙手势事件源
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ /* Qt 6 only modify */
+ auto dev = touchDevice(event->deviceId, QPointingDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case UI_AXIS_EVENT_ACTION_END:
+ case UI_AXIS_EVENT_ACTION_NONE:
+ case UI_AXIS_EVENT_ACTION_CANCEL:
+ return Qt::EndNativeGesture;
+ case UI_AXIS_EVENT_ACTION_BEGIN:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::ZoomNativeGesture;
+ };
+
+ QPointF localF = QPointF(localPos);
+ QPointF globalF = QPointF(globalPos);
+ QWindowSystemInterface::handleGestureEventWithRealValue(tlw, NSTOMS(event->timestamp), dev.data(),
+ gestureType(event->axisAction), event->pinchScale,
+ localF, globalF);
+#endif
+}
+/*!
+ * \brief 压缩合并16毫秒内的触控板轴事件
+ * \param event 触控板轴事件
+ */
+void QOhEventDispatcherPrivate::reduceWheelEvent(QSharedPointer<QtOh::WheelEvent> event)
+{
+#if OHOS_SDK_VERSION >= 15
+ if (QtOh::apiVersion() >= 15) {
+ switch(event->axisAction) {
+ case UI_AXIS_EVENT_ACTION_NONE:
+ case UI_AXIS_EVENT_ACTION_CANCEL:
+ {
+ qWarning("%s the axis event is cancel or abnormal", Q_FUNC_INFO);
+ removeWheelEvent(event);
+ }
+ break;
+ case UI_AXIS_EVENT_ACTION_END: {
+ wakeUpWheelEvent(getLastWheelEvent());
+ wakeUpWheelEvent(event);
+ removeWheelEvent(event);
+ }
+ break;
+ case UI_AXIS_EVENT_ACTION_BEGIN:
+ {
+ wakeUpWheelEvent(event);
+ }
+ break;
+ case UI_AXIS_EVENT_ACTION_UPDATE:
+ {
+ reduceWheelEventImple(event);
+ }
+ break;
+ default: break;
+ }
+ } else {
+ reduceWheelEventImple(event);
+ }
+#else
+ reduceWheelEventImple(event);
+#endif
+}
+
+void QOhEventDispatcherPrivate::reduceWheelEventImple(QSharedPointer<QtOh::WheelEvent> event)
+{
+ auto lastWheel = getLastWheelEvent();
+ if (!lastWheel.isNull()) {
+ qint64 timeDiff = event->timestamp - lastWheel->timestamp;
+ if (MSTONS(16) >= timeDiff) {
+ lastWheel->local = event->local;
+ lastWheel->global = event->global;
+ lastWheel->pinchScale *= event->pinchScale;
+ lastWheel->verticalValue += event->verticalValue;
+ lastWheel->horizontalValue += event->horizontalValue;
+ } else {
+ wakeUpWheelEvent(lastWheel);
+ replaceWheelEvent(event);
+ }
+ return;
+ }
+ wakeUpWheelEvent(lastWheel);
+ replaceWheelEvent(event);
+}
+
+void QOhEventDispatcherPrivate::mouseMove(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ bool windowChanged = (tlw != nullptr && tlw != window);
+ if (!tlw)
+ tlw = window;
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (windowChanged) {
+ _local = translatePoint(global, tlw);
+ }
+
+ //QOhWindowContext::setUnderMouseWindow(event->window, _local, global.toPoint());
+
+ /* Qt 6 only modify */
+ QWindowSystemInterface::handleMouseEvent(tlw, _local, global.toPoint(), Qt::MouseButtons(event->mouseButton),
+ Qt::NoButton, QEvent::MouseMove,
+ event->keyboardModifiers);
+}
+
+void QOhEventDispatcherPrivate::mousePress(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *grabber = QOhWindowContext::mouseGrabberWindow();
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (grabber != nullptr && grabber != window) {
+ _local = translatePoint(global, grabber);
+ }
+
+#if OHOS_SDK_VERSION < 18
+ m_button_down = event;
+#endif
+
+ //QOhWindowContext::setUnderMouseWindow(event->window, _local, global.toPoint());
+ QWindowSystemInterface::handleMouseEvent(grabber == nullptr ? window : grabber, _local, global.toPoint(), Qt::MouseButtons(event->mouseButton),
+ event->mouseButton, QEvent::MouseButtonPress,
+ event->keyboardModifiers);
+}
+
+void QOhEventDispatcherPrivate::mouseRelease(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ bool windowChanged = (tlw != nullptr && tlw != window);
+ if (!tlw)
+ tlw = window;
+
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (windowChanged) {
+ _local = translatePoint(global, tlw);
+ }
+#if OHOS_SDK_VERSION < 18
+ m_button_down.reset();
+#endif
+
+ //QOhWindowContext::setUnderMouseWindow(event->window, _local, global.toPoint());
+ QWindowSystemInterface::handleMouseEvent(tlw, _local, global.toPoint(),
+ Qt::NoButton,
+ event->mouseButton, QEvent::MouseButtonRelease,
+ event->keyboardModifiers);
+
+}
+
+void QOhEventDispatcherPrivate::nonClientAreaMouseMove(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ bool windowChanged = (tlw != nullptr && tlw != window);
+ if (!tlw)
+ tlw = window;
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (windowChanged) {
+ _local = translatePoint(global, tlw);
+ }
+
+ /* Qt 6 only modify */
+ QWindowSystemInterface::handleFrameStrutMouseEvent(tlw, _local, global.toPoint(), Qt::MouseButtons(event->mouseButton),
+ Qt::NoButton, QEvent::NonClientAreaMouseMove,
+ event->keyboardModifiers);
+}
+
+void QOhEventDispatcherPrivate::nonClientAreaMousePress(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *grabber = QOhWindowContext::mouseGrabberWindow();
+ bool windowChanged = (grabber != nullptr && grabber != window);
+ QWindow *send = grabber == nullptr ? window : grabber;
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (windowChanged) {
+ _local = translatePoint(global, send);
+ QWindowSystemInterface::handleMouseEvent(send, _local, global.toPoint(), Qt::MouseButtons(event->mouseButton),
+ event->mouseButton, QEvent::MouseButtonPress,
+ event->keyboardModifiers);
+ return;
+ }
+ /* Qt 6 only modify */
+ QWindowSystemInterface::handleFrameStrutMouseEvent(send, _local, global.toPoint(), Qt::MouseButtons(event->mouseButton),
+ event->mouseButton, QEvent::NonClientAreaMouseButtonPress,
+ event->keyboardModifiers);
+}
+
+void QOhEventDispatcherPrivate::nonClientAreaMouseRelease(QSharedPointer<QtOh::MouseEvent> event)
+{
+ QWindow *window = event->window;
+ if (window == nullptr || QOhPlatformInputContext::ohInputContext()->ignoreMouse())
+ return;
+
+ QWindow *tlw = QOhWindowContext::mouseGrabberWindow();
+ bool windowChanged = (tlw != nullptr && tlw != window);
+ if (!tlw)
+ tlw = window;
+ QPoint _local = event->xcPos.toPoint();
+ const QPointF global = event->scPos;
+ if (windowChanged) {
+ _local = translatePoint(global, tlw);
+ }
+ /* Qt 6 only modify */
+ QWindowSystemInterface::handleFrameStrutMouseEvent(tlw, _local, global.toPoint(),
+ Qt::MouseButtons(Qt::NoButton),
+ event->mouseButton, QEvent::NonClientAreaMouseButtonRelease,
+ event->keyboardModifiers);
+}
+
+QPointF QOhEventDispatcherPrivate::getLastTouchPoint(int id) const
+{
+ return m_lastTouchPoints.value(id, QPointF());
+}
+
+void QOhEventDispatcherPrivate::updateLastTouchPoints(int id, const QPointF &point)
+{
+ point.isNull() && m_lastTouchPoints.contains(id) ? void(m_lastTouchPoints.remove(id))
+ : void(m_lastTouchPoints.insert(id, point));
+}
+
+void QOhEventDispatcherPrivate::updateCursorPos(const QPoint &pt)
+{
+ auto screens = QGuiApplication::screens();
+ for (int i = 0; i < screens.count(); ++i) {
+ if (QPlatformCursor *cursor = screens.at(i)->handle()->cursor())
+ cursor->setPos(pt);
+ }
+}
+
+void QOhEventDispatcherPrivate::removeWheelEvent(QSharedPointer<QtOh::WheelEvent> event)
+{
+ if (event) {
+ QWriteLocker locker(&m_lock);
+ m_WheelEvents.remove(QtOh::EventType(event->type));
+ }
+}
+
+void QOhEventDispatcherPrivate::insertWheelEvent(QSharedPointer<QtOh::WheelEvent> event)
+{
+ if (event) {
+ QWriteLocker locker(&m_lock);
+ m_WheelEvents.insert(QtOh::EventType(event->type), event);
+ }
+}
+
+void QOhEventDispatcherPrivate::replaceWheelEvent(QSharedPointer<QtOh::WheelEvent> event)
+{
+ if (event) {
+ QWriteLocker locker(&m_lock);
+ m_WheelEvents.replace(QtOh::EventType(event->type), event);
+ }
+}
+/* Qt 6 only modify */
+QSharedPointer<QOhPointingDevice> QOhEventDispatcherPrivate::touchDevice(qint64 deviceId, QPointingDevice::DeviceType deviceType,
+ QPointingDevice::PointerType pointerType)
+{
+ for (const auto &d : m_devices) {
+ if (d->systemId() == deviceId && d->type() == deviceType
+ && d->pointerType() == pointerType) {
+ return d;
+ }
+ }
+ QPointingDevice::Capabilities capabilities = QPointingDevice::Capability::Position | QPointingDevice::Capability::Area |
+ QPointingDevice::Capability::NormalizedPosition | QPointingDevice::Capability::Pressure;
+#if 0
+ if (touchDevice->type() == QPointingDevice::DeviceType::TouchPad)
+ capabilities |= QPointingDevice::Capability::MouseEmulation;
+#endif
+ const qint64 uniqueId = deviceId | (qint64(pointerType) << 32L);
+ auto device = QSharedPointer<QOhPointingDevice>::create("ohostouch"_L1,
+ deviceId, deviceType,
+ pointerType, capabilities, 1, 3, QString(),
+ QPointingDeviceUniqueId::fromNumericId(uniqueId));
+ QWindowSystemInterface::registerInputDevice(device.data());
+ m_devices.append(device);
+ return device;
+}
+
+static void launchTimer()
+{
+ /* NOTE 执行逻辑上保证了不需要判断该单例的为空的情况 */
+ QMetaObject::invokeMethod(QOhEventDispatcher::instance(), "launchTimer");
+}
+static constexpr int SC_INTERVAL(15);
+Q_COREAPP_STARTUP_FUNCTION(launchTimer)
+QOhEventDispatcher::QOhEventDispatcher(QOhPlatformIntegration *const inte, QObject *parent)
+ : QEventDispatcherUNIX(*(new QOhEventDispatcherPrivate(inte)), parent)
+{
+ d_func()->m_qinstance.storeRelease(this);
+}
+
+QOhEventDispatcher::~QOhEventDispatcher()
+{
+ Q_D(QOhEventDispatcher);
+ if (d->m_timerId)
+ killTimer(d->m_timerId);
+
+ d->m_qinstance.storeRelease((nullptr));
+}
+
+QOhEventDispatcher *QOhEventDispatcher::instance()
+{
+ return QOhEventDispatcherPrivate::m_qinstance.loadRelaxed();
+}
+
+void QOhEventDispatcher::appendOhEvent(QtOh::EventVar var)
+{
+ Q_D(QOhEventDispatcher);
+ std::visit(*d, var);
+}
+
+void QOhEventDispatcher::updateLastTouchPoints(int id, const QPointF &point)
+{
+ Q_D(QOhEventDispatcher);
+ d->updateLastTouchPoints(id, point);
+}
+
+QPointF QOhEventDispatcher::getLastTouchPoint(int id) const
+{
+ Q_D(const QOhEventDispatcher);
+ return d->getLastTouchPoint(id);
+}
+
+#if OHOS_SDK_VERSION < 18
+void QOhEventDispatcher::handleWindowHidden(const QWindow *window)
+{
+ Q_D(QOhEventDispatcher);
+ d->handleWindowHidden(window);
+}
+#endif
+
+void QOhEventDispatcher::timerEvent(QTimerEvent *event)
+{
+ Q_D(QOhEventDispatcher);
+ if (d->m_timerId == event->timerId()) {
+ d->uniqueGeometryChangeEvent();
+ // killTimer(d->m_timerId);
+ // d->m_timerId = 0;
+ }
+ QEventDispatcherUNIX::timerEvent(event);
+}
+
+bool QOhEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags);
+ return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents;
+}
+
+void QOhEventDispatcher::launchTimer()
+{
+ Q_D(QOhEventDispatcher);
+ d->m_timerId = startTimer(SC_INTERVAL);
+}
+
+
+
new file mode 100644
@@ -0,0 +1,75 @@
+#ifndef QOHEVENTDISPATCHER_H
+#define QOHEVENTDISPATCHER_H
+
+#include <QtCore/QMutex>
+#include <QtCore/QQueue>
+#include <qsharedpointer.h>
+#include <QtCore/QSemaphore>
+#include <qpa/qwindowsysteminterface_p.h>
+#include <QtCore/private/qeventdispatcher_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define MSTONS(x) x*1000000
+#define NSTOMS(x) x/1000000
+
+class QPointingDevice; /* Qt 6 only modify */
+namespace QtOh {
+class Event;
+class WheelEvent;
+class TouchEvent;
+class MouseEvent;
+class TabletEvent;
+class GestureEvent;
+class EnterLeaveEvent;
+class GeometryChangeEvent;
+class KeyEvent;
+#if OHOS_SDK_VERSION >= 17
+class NewKeyEvent;
+#endif
+
+using EventVar = std::variant<QSharedPointer<WheelEvent>,
+ QSharedPointer<TouchEvent>,
+ QSharedPointer<MouseEvent>,
+ QSharedPointer<TabletEvent>,
+ QSharedPointer<GestureEvent>,
+ QSharedPointer<EnterLeaveEvent>,
+ QSharedPointer<GeometryChangeEvent>,
+ QSharedPointer<KeyEvent>
+ #if OHOS_SDK_VERSION >= 17
+ ,QSharedPointer<NewKeyEvent>
+ #endif
+>;
+}
+QT_END_NAMESPACE
+
+
+class QOhPlatformIntegration;
+class QOhEventDispatcherPrivate;
+class QOhEventDispatcher : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QOhEventDispatcher)
+ Q_DISABLE_COPY_MOVE(QOhEventDispatcher)
+
+public:
+ explicit QOhEventDispatcher(QOhPlatformIntegration * const inte, QObject *parent = nullptr);
+ ~QOhEventDispatcher();
+
+
+ static QOhEventDispatcher *instance();
+ void appendOhEvent(QtOh::EventVar var);
+ void updateLastTouchPoints(int id, const QPointF &point);
+ QPointF getLastTouchPoint(int id) const;
+#if OHOS_SDK_VERSION < 18
+ void handleWindowHidden(const QWindow *window);
+#endif
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+
+private:
+ Q_INVOKABLE void launchTimer();
+};
+#endif // QOHEVENTDISPATCHER_H
new file mode 100644
@@ -0,0 +1,137 @@
+#include "qohextensionwindow.h"
+#include "qjsability.h"
+#include "qohevent.h"
+#include "qohauxiliary.h"
+#include "qoheventdispatcher.h"
+#include "qohjsonlistener.h"
+#include "qjsuiextensioncontextsession.h"
+#include "qjsuiextensionability.h"
+#include "qopenharmonydefines.h"
+
+QOhExtensionWindow::QOhExtensionWindow(QOhExtensionWindow *parent)
+ : QOhNativeWindow(parent)
+{
+}
+
+QOhNativeWindow::WindowType QOhExtensionWindow::nativeWindowType() const
+{
+ return QOhNativeWindow::EXTENSION_WINDOW;
+}
+
+QJsAbility *QOhExtensionWindow::ability() const
+{
+ return m_ability;
+}
+
+int32_t QOhExtensionWindow::id() const
+{
+ return 0;
+}
+
+void QOhExtensionWindow::setWindowLimits(WindowLimits &, bool)
+{
+
+}
+
+QOhNativeWindow::WindowLimits QOhExtensionWindow::getWindowLimits()
+{
+ return WindowLimits();
+}
+
+bool QOhExtensionWindow::isFocused()
+{
+ return false;
+}
+
+bool QOhExtensionWindow::setGeometry(const QRect &rect)
+{
+ safeExec([&] {
+ QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance();
+ QSharedPointer<QtOh::GeometryChangeEvent> e(new QtOh::GeometryChangeEvent());
+ e->window = m_window->window();
+ e->rect = rect;
+ dispatcher->appendOhEvent(e);
+ });
+ return true;
+}
+
+QRect QOhExtensionWindow::geometry() const
+{
+ QRect result;
+ if (QtOh::apiVersion() >= 14) {
+ result = safeExec([this]{
+ Napi::Object properties = get("properties").As<Napi::Object>();
+ if (properties.IsObject()) {
+ Napi::Object windowRect = properties.Get("uiExtensionHostWindowProxyRect").As<Napi::Object>();
+ return QtOh::jsRect2QRect(windowRect);
+ }
+ return QRect();
+ });
+ }
+ return result;
+}
+
+QMargins QOhExtensionWindow::frameMargins() const
+{
+ return QMargins();
+}
+
+void QOhExtensionWindow::requestFocus()
+{
+
+}
+
+void QOhExtensionWindow::startListener()
+{
+ safeExec([&] {
+ if (QtOh::apiVersion() >= 14) {
+ auto listener = new QOhJsOnListener(this, "rectChange", [this](const Napi::CallbackInfo &info) {
+ if (info.Length() < 1 || !info[0].IsObject()) {
+ return;
+ }
+ Napi::Object RectChangeOptions = info[0].As<Napi::Object>();
+ Napi::Object rect = RectChangeOptions.Get("rect").As<Napi::Object>();
+ QRect qtRect = QtOh::jsRect2QRect(rect);
+ LOGI("rectChange: QRect(%{public}d, %{public}d %{public}dX%{public}d)", qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+ setGeometry(qtRect);
+ });
+ listener->setExtraArgs(1);
+ m_listeners << listener;
+ } else {
+ m_listeners << new QOhJsOnListener(this, "windowSizeChange", [this](const Napi::CallbackInfo &info) {
+ if (info.Length() < 1 || !info[0].IsObject()) {
+ return;
+ }
+ Napi::Object size = info[0].As<Napi::Object>();
+ QRect qtRect(0, 0, size.Get("width").As<Napi::Number>(), size.Get("height").As<Napi::Number>());
+ LOGI("window rect changed: QRect(%{public}d, %{public}d %{public}dX%{public}d)", qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+ // qtRect = qtRect - m;
+ // NOTE:geometry在处理事件时才会更新,此处不应判断
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GeometryChangeEvent> e(new QtOh::GeometryChangeEvent());
+ e->window = m_window->window();
+ e->rect = qtRect;
+ dispatcher->appendOhEvent(e);
+ }
+ });
+ }
+
+ QOhNativeWindow::startListener();
+ });
+}
+
+bool QOhExtensionWindow::loadContent(const QString &name)
+{
+ QOhNativeWindow::loadContent(name);
+ return safeExec([this](const QString &name){
+ QJsUIExtensionAbility *ability = dynamic_cast<QJsUIExtensionAbility *>(this->ability());
+ if (ability == nullptr)
+ return false;
+ QJsObject store(ability->newLocalStorage().As<Napi::Object>());
+ store.call("setOrCreate", {Napi::String::New(env(), "idName"), Napi::String::New(env(), name.toStdString())});
+ auto session = ability->session();
+ store.call("setOrCreate", "session", session->object());
+ session->call("loadContent", "pages/Index", store.object());
+ return true;
+ }, name);
+}
new file mode 100644
@@ -0,0 +1,36 @@
+#ifndef QOHEXTENSIONWINDOW_H
+#define QOHEXTENSIONWINDOW_H
+
+#include "qohnativewindow.h"
+
+class QOhExtensionWindow : public QOhNativeWindow
+{
+public:
+ QOhExtensionWindow(QOhExtensionWindow *parent = nullptr);
+
+ QOhNativeWindow::WindowType nativeWindowType() const override;
+
+ QJsAbility *ability() const override;
+
+ int32_t id() const override;
+
+ void setWindowLimits(WindowLimits &, bool force = false) override;
+
+ WindowLimits getWindowLimits() override;
+
+ bool isFocused() override;
+
+ bool setGeometry(const QRect &rectIn) override;
+
+ QRect geometry() const override;
+
+ QMargins frameMargins() const override;
+
+ void requestFocus() override;
+
+ void startListener() override;
+
+ virtual bool loadContent(const QString &name) override;
+};
+
+#endif // QOHEXTENSIONWINDOW_H
new file mode 100644
@@ -0,0 +1,448 @@
+#include <QString>
+#include <QByteArray>
+#include <QSharedPointer>
+#include <rawfile/raw_dir.h>
+#include <rawfile/raw_file.h>
+#include <rawfile/raw_file_manager.h>
+#include "qohnativewindowmanager.h"
+#include "qohfileenginehandler.h"
+
+
+QT_BEGIN_NAMESPACE
+typedef QVector<QString> FilesList;
+
+static const QLatin1String rawfilePrefix("rawfile:");
+const static int prefixSize = 8;
+
+static inline QString cleanRawFilePath(QString file)
+{
+ if (file.startsWith(rawfilePrefix))
+ file.remove(0, prefixSize);
+ file.replace(QLatin1String("//"), QLatin1String("/"));
+ if (file.startsWith(QLatin1Char('/')) || file.startsWith('\\'))
+ file.remove(0, 1);
+ if (file.endsWith(QLatin1Char('/')))
+ file.chop(1);
+ return file;
+}
+
+static inline QString prefixedPath(QString path)
+{
+ path = rawfilePrefix + QLatin1Char('/') + path;
+ path.replace(QLatin1String("//"), QLatin1String("/"));
+ return path;
+}
+
+struct RawFileItem {
+ enum class Type {
+ File,
+ Folder,
+ Invalid
+ };
+ RawFileItem() = default;
+ RawFileItem (const QString &rawName)
+ : name(rawName)
+ {
+ if (name.endsWith(QLatin1Char('/'))) {
+ type = Type::Folder;
+ name.chop(1);
+ }
+ }
+ Type type = Type::File;
+ QString name;
+ qint64 size = -1;
+};
+
+using RawFileItemList = QVector<RawFileItem>;
+
+class FolderIterator : public RawFileItemList
+{
+public:
+ static QSharedPointer<FolderIterator> fromCache(const QString &path, bool clone)
+ {
+ QMutexLocker lock(&m_rawFileCacheMutex);
+ QSharedPointer<FolderIterator> *folder = m_rawFilesCache.object(path);
+ if (!folder) {
+ folder = new QSharedPointer<FolderIterator>{new FolderIterator{path}};
+ if ((*folder)->empty() || !m_rawFilesCache.insert(path, folder)) {
+ QSharedPointer<FolderIterator> res = *folder;
+ delete folder;
+ return res;
+ }
+ }
+ return clone ? QSharedPointer<FolderIterator>{new FolderIterator{*(*folder)}} : *folder;
+ }
+
+ static RawFileItem::Type fileType(const QString &filePath)
+ {
+ if (filePath.isEmpty())
+ return RawFileItem::Type::Folder;
+ const QStringList paths = filePath.split(QLatin1Char('/'));
+ QString fullPath;
+ RawFileItem::Type res = RawFileItem::Type::Invalid;
+ for (const auto &path: paths) {
+ auto folder = fromCache(fullPath, false);
+ auto it = std::lower_bound(folder->begin(), folder->end(), RawFileItem{path}, [](const RawFileItem &val, const RawFileItem &RawFileItem) {
+ return val.name < RawFileItem.name;
+ });
+ if (it == folder->end() || it->name != path)
+ return RawFileItem::Type::Invalid;
+ if (!fullPath.isEmpty())
+ fullPath.append(QLatin1Char('/'));
+ fullPath += path;
+ res = it->type;
+ }
+ return res;
+ }
+
+ FolderIterator(const FolderIterator &other)
+ : RawFileItemList(other)
+ , m_index(-1)
+ , m_path(other.m_path)
+ {}
+
+ FolderIterator(const QString &path)
+ : m_path(path)
+ {
+ QByteArray fileNameByte = path.toUtf8();
+ auto *rawfileDir = OH_ResourceManager_OpenRawDir(qNativeWindowManager->resourceManager(), fileNameByte.constData());
+ if (rawfileDir) {
+ int count = OH_ResourceManager_GetRawFileCount(rawfileDir);
+ for (int i = 0; i < count; ++i) {
+ const char *fileName = OH_ResourceManager_GetRawFileName(rawfileDir, i);
+ RawFileItem item{QString::fromUtf8(fileName)};
+ insert(std::upper_bound(begin(), end(), item, [](const auto &a, const auto &b){
+ return a.name < b.name;
+ }), item);
+ }
+ OH_ResourceManager_CloseRawDir(rawfileDir);
+ }
+ m_path = rawfilePrefix + QLatin1Char('/') + m_path + QLatin1Char('/');
+ m_path.replace(QLatin1String("//"), QLatin1String("/"));
+ }
+
+ QString currentFileName() const
+ {
+ if (m_index < 0 || m_index >= size())
+ return {};
+ return at(m_index).name;
+ }
+ QString currentFilePath() const
+ {
+ if (m_index < 0 || m_index >= size())
+ return {};
+ return m_path + at(m_index).name;
+ }
+
+ bool hasNext() const
+ {
+ return !empty() && m_index + 1 < size();
+ }
+
+ std::optional<std::pair<QString, RawFileItem>> next()
+ {
+ if (!hasNext())
+ return {};
+ ++m_index;
+ return std::pair<QString, RawFileItem>(currentFileName(), at(m_index));
+ }
+
+private:
+ int m_index = -1;
+ QString m_path;
+ static QCache<QString, QSharedPointer<FolderIterator>> m_rawFilesCache;
+ static QMutex m_rawFileCacheMutex;
+};
+
+QCache<QString, QSharedPointer<FolderIterator>> FolderIterator::m_rawFilesCache(50);
+QMutex FolderIterator::m_rawFileCacheMutex;
+
+class QOhAbstractFileEngineIterator: public QAbstractFileEngineIterator
+{
+public:
+ QOhAbstractFileEngineIterator(QDir::Filters filters,
+ const QStringList &nameFilters,
+ const QString &path)
+ : QAbstractFileEngineIterator(filters, nameFilters)
+ {
+ m_currentIterator = FolderIterator::fromCache(cleanRawFilePath(path), true);
+ }
+
+ QFileInfo currentFileInfo() const override
+ {
+ return QFileInfo(currentFilePath());
+ }
+
+ QString currentFileName() const override
+ {
+ if (!m_currentIterator)
+ return {};
+ return m_currentIterator->currentFileName();
+ }
+
+ virtual QString currentFilePath() const
+ {
+ if (!m_currentIterator)
+ return {};
+ return m_currentIterator->currentFilePath();
+ }
+
+ bool hasNext() const override
+ {
+ if (!m_currentIterator)
+ return false;
+ return m_currentIterator->hasNext();
+ }
+
+ QString next() override
+ {
+ if (!m_currentIterator)
+ return {};
+ auto res = m_currentIterator->next();
+ if (!res)
+ return {};
+ return res->first;
+ }
+
+private:
+ QSharedPointer<FolderIterator> m_currentIterator;
+};
+
+class QOhRawfileEngine: public QAbstractFileEngine
+{
+public:
+ explicit QOhRawfileEngine(const QString &fileName)
+ : m_file(nullptr)
+ {
+ setFileName(fileName);
+ }
+
+ ~QOhRawfileEngine()
+ {
+ close();
+ }
+
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions = std::nullopt) override
+ {
+ if (!m_rawFile || m_rawFile->type != RawFileItem::Type::File || (openMode & QIODevice::WriteOnly))
+ return false;
+ close();
+ QByteArray fileNameByte = m_fileName.toUtf8();
+ m_file = OH_ResourceManager_OpenRawFile64(qNativeWindowManager->resourceManager(), fileNameByte.constData());
+ return m_file != nullptr;
+ }
+
+ virtual int handle() const override
+ {
+ if (m_file) {
+ RawFileDescriptor64 desc;
+ if (OH_ResourceManager_GetRawFileDescriptor64(m_file, &desc)) {
+ int result = desc.fd;
+ OH_ResourceManager_ReleaseRawFileDescriptor64(&desc);
+ return result;
+ }
+ }
+ return -1;
+ }
+
+ bool close() override
+ {
+ if (m_file) {
+ OH_ResourceManager_CloseRawFile64(m_file);
+ m_file = 0;
+ return true;
+ }
+ return false;
+ }
+
+ qint64 size() const override
+ {
+ if (m_rawFile)
+ return m_rawFile->size;
+ return -1;
+ }
+
+ qint64 pos() const override
+ {
+ if (m_file)
+ return OH_ResourceManager_GetRawFileOffset64(m_file);
+ return -1;
+ }
+
+ bool seek(qint64 pos) override
+ {
+ if (m_file)
+ return 0 == OH_ResourceManager_SeekRawFile64(m_file, pos, 0);
+ return false;
+ }
+
+ qint64 read(char *data, qint64 maxlen) override
+ {
+ if (m_file)
+ return OH_ResourceManager_ReadRawFile64(m_file, data, maxlen);
+ return -1;
+ }
+
+ bool isSequential() const override
+ {
+ return false;
+ }
+
+ bool caseSensitive() const override
+ {
+ return true;
+ }
+
+ bool isRelativePath() const override
+ {
+ return false;
+ }
+
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const override
+ {
+ FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
+ FileFlags flags;
+ if (m_rawFile) {
+ if (m_rawFile->type == RawFileItem::Type::File)
+ flags = FileType | commonFlags;
+ else if (m_rawFile->type == RawFileItem::Type::Folder)
+ flags = DirectoryType | commonFlags;
+ }
+ return type & flags;
+ }
+
+ QString fileName(FileName file = DefaultName) const override
+ {
+ int pos;
+ switch (file) {
+ case DefaultName:
+ case AbsoluteName:
+ case CanonicalName:
+ return prefixedPath(m_fileName);
+ case BaseName:
+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
+ return prefixedPath(m_fileName.mid(pos));
+ else
+ return prefixedPath(m_fileName);
+ case PathName:
+ case AbsolutePathName:
+ case CanonicalPathName:
+ if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
+ return prefixedPath(m_fileName.left(pos));
+ else
+ return prefixedPath(m_fileName);
+ default:
+ return QString();
+ }
+ }
+
+ void setFileName(const QString &file) override
+ {
+ if (m_fileName == cleanRawFilePath(file))
+ return;
+ close();
+ m_fileName = cleanRawFilePath(file);
+
+ {
+ QMutexLocker lock(&m_rawFileCacheMutex);
+ QSharedPointer<RawFileItem> *rawFilePtr = m_rawFileCache.object(m_fileName);
+ if (rawFilePtr && (*rawFilePtr)->type != RawFileItem::Type::Invalid) {
+ m_rawFile = *rawFilePtr;
+ return;
+ }
+ }
+ QSharedPointer<RawFileItem> *newRawfilePtr = new QSharedPointer<RawFileItem>(new RawFileItem);
+
+ m_rawFile = *newRawfilePtr;
+ m_rawFile->name = m_fileName;
+ m_rawFile->type = RawFileItem::Type::Invalid;
+
+ QByteArray fileNameByte = m_fileName.toUtf8();
+ bool isDir = m_fileName.isEmpty() || OH_ResourceManager_IsRawDir(qNativeWindowManager->resourceManager(), fileNameByte.constData());
+
+ if (!isDir) {
+ m_file = OH_ResourceManager_OpenRawFile64(qNativeWindowManager->resourceManager(), fileNameByte.constData());
+ if (m_file != nullptr) {
+ m_rawFile->type = RawFileItem::Type::File;
+ m_rawFile->size = OH_ResourceManager_GetRawFileSize64(m_file);
+ }
+ } else {
+ auto *rawfileDir = OH_ResourceManager_OpenRawDir(qNativeWindowManager->resourceManager(), fileNameByte.constData());
+ if (rawfileDir) {
+ m_rawFile->type = RawFileItem::Type::Folder;
+ OH_ResourceManager_CloseRawDir(rawfileDir);
+ }
+ }
+ if (m_rawFile->type != RawFileItem::Type::Invalid) {
+ QMutexLocker lock(&m_rawFileCacheMutex);
+ m_rawFileCache.insert(m_fileName, newRawfilePtr);
+ }
+ }
+
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
+ {
+ if (m_rawFile && m_rawFile->type == RawFileItem::Type::Folder)
+ return new QOhAbstractFileEngineIterator(filters, filterNames, m_fileName);
+ return nullptr;
+ }
+private:
+ RawFile64 *m_file;
+ QString m_fileName = QLatin1String(".");
+ QSharedPointer<RawFileItem> m_rawFile;
+
+ static QCache<QString, QSharedPointer<RawFileItem>> m_rawFileCache;
+ static QMutex m_rawFileCacheMutex;
+};
+
+QCache<QString, QSharedPointer<RawFileItem>> QOhRawfileEngine::m_rawFileCache(200);
+QMutex QOhRawfileEngine::m_rawFileCacheMutex;
+
+
+QOhFileEngineHandler::QOhFileEngineHandler()
+{
+
+}
+
+QAbstractFileEngine *QOhFileEngineHandler::create(const QString &fileName) const
+{
+ if (fileName.isEmpty())
+ return 0;
+ if (!fileName.startsWith(rawfilePrefix))
+ return 0;
+
+ QString _fileName = cleanRawFilePath(fileName);
+ return new QOhRawfileEngine(_fileName);
+// if (byteArray.isEmpty() && OH_ResourceManager_IsRawDir(qNativeWindowManager->resourceManager(), byteArray.constData())) {
+// return new QOhRawfileEngine(fileName);
+
+// ) {
+// /* 处理目录 */
+// m_rawfileCacheMutext.lock();
+// QSharedPointer<QOhRawDir> *ohrd = m_rawDirCache.object(_fileName.toLatin1());
+// m_rawfileCacheMutext.unlock();
+
+// if (!ohrd) {
+// if (!m_hasPrepopulatedCache) {
+// RawDir *rd = OH_ResourceManager_OpenRawDir(qNativeWindowManager->resourceManager(), byteArray.constData());
+// if (rd) {
+// ohrd = new QSharedPointer<QOhRawDir>(new QOhRawDir(rd));
+// m_rawfileCacheMutext.lock();
+// m_rawDirCache.insert(_fileName.toLatin1(), ohrd);
+// m_rawfileCacheMutext.unlock();
+
+// }
+// }
+// } else {
+// return new QOhRawfileEngine(*ohrd, fileName);
+// }
+// } else {
+// /* 处理文件 */
+// RawFile *file = OH_ResourceManager_OpenRawFile(qNativeWindowManager->resourceManager(), byteArray.constData());
+// if (file != nullptr) {
+
+// }
+// }
+// return 0;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QOHFILEENGINEHANDLER_H
+#define QOHFILEENGINEHANDLER_H
+
+#include <QtCore/private/qabstractfileengine_p.h>
+#include <QString>
+#include <QCache>
+#include <QMutex>
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+struct QOhRawDir;
+class QOhFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ QOhFileEngineHandler();
+ virtual QAbstractFileEngine *create(const QString &fileName) const override;
+private:
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHFILEENGINEHANDLER_H
new file mode 100644
@@ -0,0 +1,430 @@
+#include "qohevent.h"
+#include "qohgesturemanager.h"
+#include "qoheventdispatcher.h"
+#include <qopenharmonydefines.h>
+#include <arkui/native_interface.h>
+
+#include "qohkeys.h"
+#include "qohdisplaymanager.h"
+#include "qohplatformscreen.h"
+#include "qohplatforminputcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhGestureManager::QOhGestureManager()
+{
+
+}
+
+void QOhGestureManager::createGestureRecognizerForNode(ArkUI_NodeHandle node)
+{
+ auto gestureApi = reinterpret_cast<ArkUI_NativeGestureAPI_1 *>(
+ OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_GESTURE, "ArkUI_NativeGestureAPI_1"));
+ /* 创建手势组 */
+ auto groupGesture = gestureApi->createGroupGesture(ArkUI_GroupGestureMode::PARALLEL_GROUP);
+
+ /* 创建拖动手势 */
+ auto panGesture = gestureApi->createPanGesture(1, GESTURE_DIRECTION_ALL, 5);
+ int32_t result = gestureApi->setGestureEventTarget(panGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhGestureManager::handlePanGestureEvent);
+
+ if (0 != result)
+ LOGE("set panGesture Target Failed.");
+
+ /* 创建捏合手势 */
+ auto pinchGesture = gestureApi->createPinchGesture(2, 0);
+ result = gestureApi->setGestureEventTarget(pinchGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhGestureManager::handlePinchGestureEvent);
+ if (0 != result)
+ LOGE("set pinchGesture Target Failed.");
+
+ /* 创建滑动手势 */
+ auto swipeGesture = gestureApi->createSwipeGesture(1, GESTURE_DIRECTION_ALL, 0);
+ result = gestureApi->setGestureEventTarget(swipeGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhGestureManager::handleSwipeGestureEvent);
+ if (0 != result)
+ LOGE("set swipeGesture Target Failed.");
+
+ /* 创建旋转手势 */
+ auto rotationGesture = gestureApi->createRotationGesture(2, 0);
+ result = gestureApi->setGestureEventTarget(rotationGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhGestureManager::handleRotationGestureEvent);
+
+ /* 创建长按手势 */
+ auto longPressGesture = gestureApi->createLongPressGesture(1, false, 500);
+ gestureApi->setGestureEventTarget(longPressGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhGestureManager::handleLongPressGesture);
+
+ /* 将手势添加到手势组 */
+ if (gestureApi->addChildGesture) {
+ /* FIXME panGesture造成无边框时三键位置无响应 */
+ //gestureApi->addChildGesture(groupGesture, panGesture);
+ gestureApi->addChildGesture(groupGesture, pinchGesture);
+ gestureApi->addChildGesture(groupGesture, longPressGesture);
+ //gestureApi->addChildGesture(groupGesture, swipeGesture);
+ //gestureApi->addChildGesture(groupGesture, rotationGesture);
+ }
+
+ /* 将手势组设置到组件上 */
+ gestureApi->addGestureToNode(node, groupGesture, PARALLEL, NORMAL_GESTURE_MASK);
+}
+
+void QOhGestureManager::setWindow(QWindow *w)
+{
+ m_window = w;
+}
+
+bool QOhGestureManager::isSupported()
+{
+ auto gestureApi = reinterpret_cast<ArkUI_NativeGestureAPI_1 *>(
+ OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_GESTURE, "ArkUI_NativeGestureAPI_1"));
+ return (gestureApi->createGroupGesture != nullptr);
+}
+
+bool QOhGestureManager::isValidEvent(ArkUI_GestureEvent *event, QOhGestureManager *manager)
+{
+ if ((nullptr == event) || (nullptr == manager) ||
+ manager->m_window.isNull())
+ return false;
+ return true;
+}
+
+static QPointF fix_global_point(const QPointF &pt, int32_t displayId)
+{
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ QPointF result = pt;
+ if (platformScreen) {
+ result += platformScreen->geometry().topLeft();
+ }
+ return result;
+}
+void QOhGestureManager::handlePanGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhGestureManager *manager = reinterpret_cast<QOhGestureManager*>(extraParam);
+ if (!isValidEvent(event, manager))
+ return;
+
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ float velocity = OH_ArkUI_PanGesture_GetVelocity(event);
+
+ /* GESTURE_EVENT_ACTION_END = 0x04
+ * GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::PanNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = velocity;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx,gy), displayId);
+ e->gestureType = gestureType(actionType);
+ e->window = manager->m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhGestureManager::handleLongPressGesture(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhGestureManager *manager = reinterpret_cast<QOhGestureManager*>(extraParam);
+ if (!isValidEvent(event, manager))
+ return;
+
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ float scale = OH_ArkUI_PinchGesture_GetScale(event);
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+ bool mouseTriggered = (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType || UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType);
+ /* NOTE 将长按手势绑定到Qt的QContextMenu事件
+ * 只处理开始的通知,避免重复发送事件通知
+ */
+ if (GESTURE_EVENT_ACTION_ACCEPT == actionType) {
+ // Todo 从event 获取mods
+ QWindowSystemInterface::handleContextMenuEvent(manager->m_window, mouseTriggered, QPointF(lx, ly).toPoint(), fix_global_point(QPointF(gx, gy), displayId).toPoint(),
+ QOhKeys::keyboardModifiers());
+ }
+}
+
+void QOhGestureManager::handlePinchGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhGestureManager *manager = reinterpret_cast<QOhGestureManager*>(extraParam);
+ if (!isValidEvent(event, manager))
+ return;
+
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ /* 过滤来自鼠标/触控板的系统手势 */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType ||
+ UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType) {
+ return;
+ }
+
+ float scale = OH_ArkUI_PinchGesture_GetScale(event);
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::ZoomNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = scale;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->gestureType = gestureType(actionType);
+ e->window = manager->m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhGestureManager::handleSwipeGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhGestureManager *manager = reinterpret_cast<QOhGestureManager*>(extraParam);
+ if (!isValidEvent(event, manager))
+ return;
+
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ float angle = OH_ArkUI_SwipeGesture_GetAngle(event);
+ float velocity = OH_ArkUI_SwipeGesture_GetVelocity(event);
+
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::SwipeNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = velocity;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->gestureType = gestureType(actionType);
+ e->window = manager->m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhGestureManager::handleRotationGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhGestureManager *manager = reinterpret_cast<QOhGestureManager*>(extraParam);
+ if (!isValidEvent(event, manager))
+ return;
+
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+#if 1
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ /* 过滤来自鼠标/触控板的系统手势 */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType ||
+ UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType) {
+ return;
+ }
+#endif
+ /* GESTURE_EVENT_ACTION_END = 0x04
+ * GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ float angle = OH_ArkUI_RotationGesture_GetAngle(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::RotateNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = angle;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->gestureType = gestureType(actionType);
+ e->window = manager->m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,58 @@
+#include <QDebug>
+
+#include "qopenharmony.h"
+#include "qohjsonlistener.h"
+#include "qohnativewindow.h"
+
+QT_BEGIN_NAMESPACE
+
+void QOhJsOnListener::on()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if (m_args != nullptr) {
+ std::vector<napi_value> args;
+ args.push_back(QNapi::create(m_event));
+ std::vector<Napi::Reference<Napi::Value>> extras;
+ m_args->toNapiValue(extras);
+ for (auto &arg : extras) {
+ args.push_back(arg.Value());
+ }
+ args.push_back(m_callback.Value());
+ Napi::Function on = QNapi::get<Napi::Function>(m_object->object(), "on");
+ on.Call(m_object->object(), args);
+ } else {
+ m_object->call("on", {Napi::String::New(m_object->env(), m_event.toStdString()), m_callback.Value()});
+ }
+ });
+}
+
+void QOhJsOnListener::off()
+{
+ // windowWillClose会打印This window state is abnormal.错误信息?
+ if (m_callback.IsEmpty() || m_event == "windowWillClose")
+ return;
+ while (m_isRunning.loadRelaxed()) {
+ QGuiApplication::processEvents();
+ }
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_object->call("off", {Napi::String::New(m_object->env(), m_event.toStdString()), m_callback.Value()});
+ });
+ deleteArgs();
+}
+
+void QOhJsOnListener::deleteArgs()
+{
+ if (m_args != nullptr) {
+ delete m_args;
+ m_args = nullptr;
+ }
+}
+
+QOhJsOnListener::~QOhJsOnListener()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_callback.Reset();
+ });
+}
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,74 @@
+#ifndef QOHJSONLISTENER_H
+#define QOHJSONLISTENER_H
+
+#include <QAtomicInteger>
+#include <functional>
+#include <QJsObject>
+#include <qnapi.h>
+#include <QScopedPointer>
+#include <private/qopenharmony_p.h>
+QT_BEGIN_NAMESPACE
+
+class QOhJsOnListener
+{
+ struct Args{
+ virtual void toNapiValue(std::vector<Napi::Reference<Napi::Value>> &out) const = 0;
+ virtual ~Args() {}
+ };
+ template<typename... Params>
+ struct OnArgs : Args
+ {
+ OnArgs(Params&&... args): argsTuple(std::forward<Params>(args)...) {}
+ std::tuple<Params...> argsTuple;
+ virtual void toNapiValue(std::vector<Napi::Reference<Napi::Value>> &out) const override
+ {
+ std::apply([&out](const auto&... args) {
+ (out.push_back(Napi::Persistent(QNapi::create(args))), ...);
+ }, argsTuple);
+ }
+ };
+public:
+ template<typename Function>
+ QOhJsOnListener(QJsObject *object, const QString &event, Function&& handler)
+ : m_object(object)
+ , m_event(event)
+ {
+ m_isRunning.storeRelaxed(false);
+ m_callback = QtOh::runOnJsUIThreadWithResult([this, function = std::forward<Function>(handler)](){
+ Napi::Function func = QNapi::create([this, function](const Napi::CallbackInfo& info) {
+ m_isRunning.storeRelaxed(true);
+ if constexpr (std::is_void_v<std::invoke_result_t<Function, Napi::CallbackInfo>>) {
+ function(info);
+ m_isRunning.storeRelaxed(false);
+ } else {
+ auto result = function(info);
+ m_isRunning.storeRelaxed(false);
+ return result;
+ }
+ });
+ Napi::FunctionReference result = Napi::Persistent(func);
+ return result;
+ });
+ }
+ ~QOhJsOnListener();
+
+ void on();
+ void off();
+ template<typename... Args>
+ void setExtraArgs(Args&&... args)
+ {
+ deleteArgs();
+ m_args = new OnArgs<Args...>(std::forward<Args>(args)...);
+ }
+
+private:
+ void deleteArgs();
+ QJsObject *m_object;
+ QString m_event;
+ QAtomicInteger<bool> m_isRunning;
+ Args *m_args = nullptr;
+ Napi::FunctionReference m_callback;
+};
+
+QT_END_NAMESPACE
+#endif // QOHJSONLISTENER_H
new file mode 100644
@@ -0,0 +1,845 @@
+#include "qohkeys.h"
+#include "qohwindowcontext.h"
+#include "qohobjectholder.h"
+
+#include <QtCore/QHash>
+#include <qpa/qwindowsysteminterface.h>
+QT_BEGIN_NAMESPACE
+
+struct KeyRecord {
+ KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {}
+ KeyRecord() {}
+
+ int code;
+ int ascii;
+ int state;
+ QString text;
+};
+
+// We need to record the pressed keys in order to decide, whether the key event is an autorepeat
+// event. As soon as its state changes, the chain of autorepeat events will be broken.
+static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
+struct KeyRecorder
+{
+ inline KeyRecord *findKey(int32_t code, bool remove);
+ inline void storeKey(int32_t code, int ascii, int state, const QString& text);
+ inline void clearKeys();
+
+ int nrecs = 0;
+ KeyRecord deleted_record; // A copy of last entry removed from records[]
+ KeyRecord records[QT_MAX_KEY_RECORDINGS];
+};
+
+static KeyRecorder key_recorder;
+
+static void clearKeyRecorderOnApplicationInActive(Qt::ApplicationState state)
+{
+ if (state == Qt::ApplicationInactive)
+ key_recorder.clearKeys();
+}
+
+KeyRecord *KeyRecorder::findKey(int32_t code, bool remove)
+{
+ KeyRecord *result = nullptr;
+ for (int i = 0; i < nrecs; ++i) {
+ if (records[i].code == code) {
+ if (remove) {
+ deleted_record = records[i];
+ // Move rest down, and decrease count
+ while (i + 1 < nrecs) {
+ records[i] = records[i + 1];
+ ++i;
+ }
+ --nrecs;
+ result = &deleted_record;
+ } else {
+ result = &records[i];
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+void KeyRecorder::storeKey(int32_t code, int ascii, int state, const QString& text)
+{
+ Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
+ "Internal KeyRecorder",
+ "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
+
+ if (nrecs == QT_MAX_KEY_RECORDINGS) {
+ qWarning("Qt: Internal keyboard buffer overflow");
+ return;
+ }
+ records[nrecs++] = KeyRecord(code,ascii,state,text);
+}
+
+void KeyRecorder::clearKeys()
+{
+ nrecs = 0;
+}
+
+bool QOhKeys::isKeyPressed(int32_t keyCode)
+{
+ bool ret = false;
+ QOhObjectHolder<Input_KeyState> keyState(OH_Input_CreateKeyState, OH_Input_DestroyKeyState);
+ Input_KeyState *input_key_state = keyState.object();
+ OH_Input_SetKeyCode(input_key_state, keyCode);
+ Input_Result result = OH_Input_GetKeyState(input_key_state);
+ if (INPUT_SUCCESS == result) {
+ int iPressed = OH_Input_GetKeyPressed(input_key_state);
+ ret = (KEY_PRESSED == iPressed);
+ }
+ return ret;
+}
+
+bool QOhKeys::isKeyLockActive(int32_t keyCode)
+{
+ bool ret = false;
+ static QOhObjectHolder<Input_KeyState> keyState(OH_Input_CreateKeyState, OH_Input_DestroyKeyState);
+ static Input_KeyState *input_key_state = keyState.object();
+ OH_Input_SetKeyCode(input_key_state, keyCode);
+ Input_Result result = OH_Input_GetKeyState(input_key_state);
+ if (INPUT_SUCCESS == result) {
+ int iSwitch = OH_Input_GetKeySwitch(input_key_state);
+ ret = (KEY_SWITCH_ON == iSwitch);
+ }
+ return ret;
+}
+
+Qt::Key QOhKeys::ohCode2QtKey(int32_t keyCode, Qt::KeyboardModifiers modifiers)
+{
+ static QHash<int32_t, Qt::Key> sKeyMap
+ {
+ { KEY_HOME, Qt::Key_Home },
+ { KEY_BACK, Qt::Key_Back },
+ { KEY_MEDIA_PLAY_PAUSE, Qt::Key_MediaTogglePlayPause },
+ { KEY_MEDIA_STOP, Qt::Key_MediaStop },
+ { KEY_MEDIA_NEXT, Qt::Key_MediaNext },
+ { KEY_MEDIA_PREVIOUS, Qt::Key_MediaPrevious },
+ { KEY_MEDIA_REWIND, Qt::Key_AudioRewind },
+ { KEY_MEDIA_FAST_FORWARD, Qt::Key_AudioForward },
+ { KEY_VOLUME_UP, Qt::Key_VolumeUp },
+ { KEY_VOLUME_DOWN, Qt::Key_VolumeDown },
+ { KEY_POWER, Qt::Key_PowerDown },
+ { KEY_CAMERA, Qt::Key_Camera },
+ { KEY_VOLUME_MUTE, Qt::Key_VolumeMute },
+ { KEY_MUTE, Qt::Key_MicMute },
+ { KEY_BRIGHTNESS_UP, Qt::Key_MonBrightnessUp },
+ { KEY_BRIGHTNESS_DOWN, Qt::Key_MonBrightnessDown },
+ { KEY_0, Qt::Key_0 },
+ { KEY_1, Qt::Key_1 },
+ { KEY_2, Qt::Key_2 },
+ { KEY_3, Qt::Key_3 },
+ { KEY_4, Qt::Key_4 },
+ { KEY_5, Qt::Key_5 },
+ { KEY_6, Qt::Key_6 },
+ { KEY_7, Qt::Key_7 },
+ { KEY_8, Qt::Key_8 },
+ { KEY_9, Qt::Key_9 },
+ { KEY_STAR, Qt::Key_Asterisk },
+ { KEY_POUND, Qt::Key_NumberSign },
+ { KEY_DPAD_UP, Qt::Key_Up },
+ { KEY_DPAD_DOWN, Qt::Key_Down },
+ { KEY_DPAD_LEFT, Qt::Key_Left },
+ { KEY_DPAD_RIGHT, Qt::Key_Right },
+ //{ KEY_DPAD_CENTER, Qt::Key_ },
+ { KEY_A, Qt::Key_A },
+ { KEY_B, Qt::Key_B },
+ { KEY_C, Qt::Key_C },
+ { KEY_D, Qt::Key_D },
+ { KEY_E, Qt::Key_E },
+ { KEY_F, Qt::Key_F },
+ { KEY_G, Qt::Key_G },
+ { KEY_H, Qt::Key_H },
+ { KEY_I, Qt::Key_I },
+ { KEY_J, Qt::Key_J },
+ { KEY_K, Qt::Key_K },
+ { KEY_L, Qt::Key_L },
+ { KEY_M, Qt::Key_M },
+ { KEY_N, Qt::Key_N },
+ { KEY_O, Qt::Key_O },
+ { KEY_P, Qt::Key_P },
+ { KEY_Q, Qt::Key_Q },
+ { KEY_R, Qt::Key_R },
+ { KEY_S, Qt::Key_S },
+ { KEY_T, Qt::Key_T },
+ { KEY_U, Qt::Key_U },
+ { KEY_V, Qt::Key_V },
+ { KEY_W, Qt::Key_W },
+ { KEY_X, Qt::Key_X },
+ { KEY_Y, Qt::Key_Y },
+ { KEY_Z, Qt::Key_Z },
+ { KEY_COMMA, Qt::Key_Comma },
+ { KEY_PERIOD, Qt::Key_Period },
+ { KEY_ALT_LEFT, Qt::Key_Alt },
+ { KEY_ALT_RIGHT, Qt::Key_Alt },
+ { KEY_SHIFT_LEFT, Qt::Key_Shift },
+ { KEY_SHIFT_RIGHT, Qt::Key_Shift },
+ { KEY_TAB, Qt::Key_Tab },
+ { KEY_SPACE, Qt::Key_Space },
+ //{ KEY_SYM, Qt::KEY_},
+ { KEY_EXPLORER, Qt::Key_Explorer },
+ //{ KEY_ENVELOPE, Qt::Key_ },
+ { KEY_ENTER, Qt::Key_Return },
+ { KEY_DEL, Qt::Key_Backspace },
+ { KEY_GRAVE, Qt::Key_QuoteLeft },
+ { KEY_MINUS, Qt::Key_Minus },
+ { KEY_EQUALS, Qt::Key_Equal },
+ { KEY_LEFT_BRACKET, Qt::Key_BracketLeft },
+ { KEY_RIGHT_BRACKET, Qt::Key_BracketRight },
+ { KEY_BACKSLASH, Qt::Key_Backslash },
+ { KEY_SEMICOLON, Qt::Key_Semicolon },
+ { KEY_APOSTROPHE, Qt::Key_Apostrophe },
+ { KEY_SLASH, Qt::Key_Slash },
+ { KEY_AT, Qt::Key_At },
+ { KEY_PLUS, Qt::Key_Plus },
+ { KEY_MENU, Qt::Key_Menu },
+ { KEY_PAGE_UP, Qt::Key_PageUp },
+ { KEY_PAGE_DOWN, Qt::Key_PageDown },
+ { KEY_ESCAPE, Qt::Key_Escape },
+ { KEY_FORWARD_DEL, Qt::Key_Delete },
+ { KEY_CTRL_LEFT, Qt::Key_Control },
+ { KEY_CTRL_RIGHT, Qt::Key_Control },
+ { KEY_CAPS_LOCK, Qt::Key_CapsLock },
+ { KEY_SCROLL_LOCK, Qt::Key_ScrollLock },
+ { KEY_META_LEFT, Qt::Key_Meta },
+ { KEY_META_RIGHT, Qt::Key_Meta },
+ //{ KEY_FUNCTION, Qt::Key_ },
+ { KEY_SYSRQ, Qt::Key_SysReq },
+ { KEY_BREAK, Qt::Key_Pause },
+ { KEY_MOVE_HOME, Qt::Key_Home },
+ { KEY_MOVE_END, Qt::Key_End },
+ { KEY_INSERT, Qt::Key_Insert },
+ { KEY_FORWARD, Qt::Key_Forward },
+ { KEY_MEDIA_PLAY, Qt::Key_MediaPlay },
+ { KEY_MEDIA_PAUSE, Qt::Key_MediaPause },
+ //{ KEY_MEDIA_CLOSE, Qt::Key_Media },
+ //{ KEY_MEDIA_EJECT, Qt::Key_Eject },
+ { KEY_MEDIA_RECORD, Qt::Key_MediaRecord },
+ { KEY_F1, Qt::Key_F1 },
+ { KEY_F2, Qt::Key_F2 },
+ { KEY_F3, Qt::Key_F3 },
+ { KEY_F4, Qt::Key_F4 },
+ { KEY_F5, Qt::Key_F5 },
+ { KEY_F6, Qt::Key_F6 },
+ { KEY_F7, Qt::Key_F7 },
+ { KEY_F8, Qt::Key_F8 },
+ { KEY_F9, Qt::Key_F9 },
+ { KEY_F10, Qt::Key_F10 },
+ { KEY_F11, Qt::Key_F11 },
+ { KEY_F12, Qt::Key_F12 },
+ { KEY_NUM_LOCK, Qt::Key_NumLock },
+ { KEY_NUMPAD_0, Qt::Key_0 },
+ { KEY_NUMPAD_1, Qt::Key_1 },
+ { KEY_NUMPAD_2, Qt::Key_2 },
+ { KEY_NUMPAD_3, Qt::Key_3 },
+ { KEY_NUMPAD_4, Qt::Key_4 },
+ { KEY_NUMPAD_5, Qt::Key_5 },
+ { KEY_NUMPAD_6, Qt::Key_6 },
+ { KEY_NUMPAD_7, Qt::Key_7 },
+ { KEY_NUMPAD_8, Qt::Key_8 },
+ { KEY_NUMPAD_9, Qt::Key_9 },
+ { KEY_NUMPAD_DIVIDE, Qt::Key_Slash },
+ { KEY_NUMPAD_MULTIPLY, Qt::Key_Asterisk },
+ { KEY_NUMPAD_SUBTRACT, Qt::Key_Minus },
+ { KEY_NUMPAD_ADD, Qt::Key_Plus },
+ { KEY_NUMPAD_DOT, Qt::Key_Period },
+ { KEY_NUMPAD_COMMA, Qt::Key_Comma },
+ { KEY_NUMPAD_ENTER, Qt::Key_Enter },
+ { KEY_NUMPAD_EQUALS, Qt::Key_Equal },
+ { KEY_NUMPAD_LEFT_PAREN, Qt::Key_ParenLeft },
+ { KEY_NUMPAD_RIGHT_PAREN, Qt::Key_ParenRight },
+ //{ KEY_VIRTUAL_MULTITASK, Qt::Key_ },
+ { KEY_SLEEP, Qt::Key_Sleep },
+ { KEY_ZENKAKU_HANKAKU, Qt::Key_Zenkaku_Hankaku },
+ //{ KEY_102ND, Qt::Key_ },
+ //{ KEY_RO, Qt::Key_ },
+ { KEY_KATAKANA, Qt::Key_Katakana },
+ { KEY_HIRAGANA, Qt::Key_Hiragana },
+ { KEY_HENKAN, Qt::Key_Henkan },
+ { KEY_KATAKANA_HIRAGANA, Qt::Key_Hiragana_Katakana },
+ { KEY_MUHENKAN, Qt::Key_Muhenkan },
+ { KEY_LINEFEED, Qt::Key_Enter },
+ //{ KEY_MACRO, Qt::Key_macron },
+ //{ KEY_NUMPAD_PLUSMINUS, Qt::Key_ },
+ //{ KEY_SCALE, Qt::Key_ },
+ { KEY_HANGUEL, Qt::Key_Hangul },
+ { KEY_HANJA, Qt::Key_Hangul_Hanja },
+ { KEY_YEN, Qt::Key_yen },
+ { KEY_STOP, Qt::Key_Stop },
+ //{ KEY_AGAIN, Qt::Key_ },
+ //{ KEY_PROPS, Qt::Key_ },
+ { KEY_UNDO, Qt::Key_Undo },
+ { KEY_COPY, Qt::Key_Copy },
+ { KEY_OPEN, Qt::Key_Open },
+ { KEY_PASTE, Qt::Key_Paste },
+ { KEY_FIND, Qt::Key_Find },
+ { KEY_CUT, Qt::Key_Cut },
+ { KEY_HELP, Qt::Key_Help },
+ //{ KEY_CALC, Qt::Key_ },
+ //{ KEY_FILE, Qt::Key_ },
+ //{ KEY_BOOKMARKS, Qt::Key_Book },
+ //{ KEY_NEXT, Qt::Key_ },
+ //{ KEY_PLAYPAUSE, Qt::Key_MediaTogglePlayPause }
+ //{ KEY_PREVIOUS, Qt::Key_ },
+ //{ KEY_STOPCD, Qt::Key_ },
+ //{ KEY_CONFIG, Qt::Key_ },
+ { KEY_REFRESH, Qt::Key_Refresh },
+ { KEY_EXIT, Qt::Key_Exit },
+ //{ KEY_EDIT, Qt::Key_ },
+ //{ KEY_SCROLLUP, Qt::Key_ },
+ //{ KEY_SCROLLDOWN, Qt::Key_ },
+ { KEY_NEW, Qt::Key_New },
+ { KEY_REDO, Qt::Key_Redo },
+ { KEY_CLOSE, Qt::Key_Close },
+ { KEY_PLAY, Qt::Key_Play },
+ //{ KEY_BASSBOOST, Qt::Key_ },
+ { KEY_PRINT, Qt::Key_Print },
+ //{ KEY_CHAT, Qt::Key_ },
+ { KEY_FINANCE, Qt::Key_Finance },
+ { KEY_CANCEL, Qt::Key_Cancel },
+ //{ KEY_KBDILLUM_TOGGLE, Qt::Key_ },
+ { KEY_KBDILLUM_DOWN, Qt::Key_KeyboardBrightnessDown },
+ { KEY_KBDILLUM_UP, Qt::Key_KeyboardBrightnessUp },
+ { KEY_SEND, Qt::Key_Send },
+ { KEY_REPLY, Qt::Key_Reply },
+ { KEY_FORWARDMAIL, Qt::Key_Forward },
+ { KEY_SAVE, Qt::Key_Save },
+ { KEY_DOCUMENTS, Qt::Key_Documents },
+ //{ KEY_VIDEO_NEXT, Qt::Key_ },
+ //{ KEY_VIDEO_PREV, Qt::Key_ },
+ //{ KEY_BRIGHTNESS_CYCLE, Qt::Key_ },
+ //{ KEY_BRIGHTNESS_ZERO, Qt::Key_ },
+ //{ KEY_DISPLAY_OFF, Qt::Key_ },
+ //{ KEY_BTN_MISC, Qt::Key_ },
+ { KEY_GOTO, Qt::Key_Go },
+ { KEY_INFO, Qt::Key_Info },
+ //{ KEY_PROGRAM, Qt::Key_ },
+ //{ KEY_PVR, Qt::Key_ },
+ { KEY_SUBTITLE, Qt::Key_Subtitle },
+ //{ KEY_FULL_SCREEN, Qt::Key_ },
+ //{ KEY_KEYBOARD, Qt::Key_ },
+ //{ KEY_ASPECT_RATIO, Qt::Key_ },
+ //{ KEY_PC, Qt::Key_ },
+ //{ KEY_TV, Qt::Key_ },
+ //{ KEY_TV2, Qt::Key_ },
+ //{ KEY_VCR, Qt::Key_ },
+ //{ KEY_VCR2, Qt::Key_ },
+ //{ KEY_SAT, Qt::Key_ },
+ { KEY_CD, Qt::Key_CD },
+ //{ KEY_TAPE, Qt::Key_ },
+ //{ KEY_TUNER, Qt::Key_ },
+ { KEY_PLAYER, Qt::Key_Play },
+ //{ KEY_DVD, Qt::Key_ },
+ //{ KEY_AUDIO, Qt::Key_ },
+ { KEY_VIDEO, Qt::Key_Video },
+ { KEY_MEMO, Qt::Key_Memo },
+ { KEY_CALENDAR, Qt::Key_Calendar },
+ { KEY_RED, Qt::Key_Red },
+ { KEY_GREEN, Qt::Key_Green },
+ { KEY_YELLOW, Qt::Key_Yellow },
+ { KEY_BLUE, Qt::Key_Blue },
+ { KEY_CHANNELUP, Qt::Key_ChannelUp },
+ { KEY_CHANNELDOWN, Qt::Key_ChannelDown },
+ //{ KEY_LAST, Qt::Key_ },
+ //{ KEY_RESTART, Qt::Key_ },
+ //{ KEY_SLOW, Qt::Key_ },
+ //{ KEY_SHUFFLE, Qt::Key_ },
+ //{ KEY_VIDEOPHONE, Qt::Key_ },
+ { KEY_GAMES, Qt::Key_Game },
+ { KEY_ZOOMIN, Qt::Key_ZoomIn },
+ { KEY_ZOOMOUT, Qt::Key_ZoomOut },
+ //{ KEY_ZOOMRESET, Qt::Key_ },
+ //{ KEY_WORDPROCESSOR, Qt::Key_ },
+ //{ KEY_EDITOR, Qt::Key_ },
+ //{ KEY_SPREADSHEET, Qt::Key_ },
+ //{ KEY_GRAPHICSEDITOR, Qt::Key_ },
+ //{ KEY_PRESENTATION, Qt::Key_ },
+ //{ KEY_DATABASE, Qt::Key_ },
+ { KEY_NEWS, Qt::Key_News },
+ //{ KEY_VOICEMAIL, Qt::Key_ },
+ //{ KEY_ADDRESSBOOK, Qt::Key_ },
+ { KEY_MESSENGER, Qt::Key_Messenger },
+ //{ KEY_BRIGHTNESS_TOGGLE, Qt::Key_BrightnessAdjust }
+ //{ KEY_SPELLCHECK, Qt::Key_Spell }
+ //{ KEY_COFFEE, Qt::Key_ },
+ //{ KEY_MEDIA_REPEAT, Qt::Key_ },
+ //{ KEY_IMAGES, Qt::Key_ },
+ //{ KEY_BUTTONCONFIG, Qt::Key_ },
+ //{ KEY_TASKMANAGER, Qt::Key_TaskPane }
+ //{ KEY_JOURNAL, Qt::Key_ },
+ //{ KEY_CONTROLPANEL, Qt::Key_ },
+ //{ KEY_APPSELECT, Qt::Key_ },
+ { KEY_SCREENSAVER, Qt::Key_ScreenSaver },
+ //{ KEY_ASSISTANT, Qt::Key_ },
+ //{ KEY_KBD_LAYOUT_NEXT, Qt::Key_ },
+ //{ KEY_BRIGHTNESS_MIN, Qt::Key_ },
+ //{ KEY_BRIGHTNESS_MAX, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_PREV, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_NEXT, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_PREVGROUP, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_NEXTGROUP, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_ACCEPT, Qt::Key_ },
+ //{ KEY_KBDINPUTASSIST_CANCEL, Qt::Key_ },
+ //{ KEY_FRONT, Qt::Key_ }
+ //{ KEY_SETUP, Qt::Key_Settings },
+ { KEY_WAKEUP, Qt::Key_WakeUp },
+ //{ KEY_SENDFILE, Qt::Key_ },
+ //{ KEY_DELETEFILE, Qt::Key_ },
+ { KEY_XFER, Qt::Key_Xfer },
+ //{ KEY_PROG1, Qt::Key_ },
+ //{ KEY_PROG2, Qt::Key_ },
+ //{ KEY_MSDOS, Qt::Key_ },
+ //{ KEY_SCREENLOCK, Qt::Key_ },
+ //{ KEY_DIRECTION_ROTATE_DISPLAY, Qt::Key },
+ //{ KEY_CYCLEWINDOWS, Qt::Key_ },
+ //{ KEY_COMPUTER, Qt::Key },
+ { KEY_EJECTCLOSECD, Qt::Key_Eject },
+ //{ KEY_ISO, Qt::Key_ },
+ //{ KEY_MOVE, Qt::Key_ },
+ { KEY_F13, Qt::Key_F13 },
+ { KEY_F14, Qt::Key_F14 },
+ { KEY_F15, Qt::Key_F15 },
+ { KEY_F16, Qt::Key_F16 },
+ { KEY_F17, Qt::Key_F17 },
+ { KEY_F18, Qt::Key_F18 },
+ { KEY_F19, Qt::Key_F19 },
+ { KEY_F20, Qt::Key_F20 },
+ { KEY_F21, Qt::Key_F21 },
+ { KEY_F22, Qt::Key_F22 },
+ { KEY_F23, Qt::Key_F23 },
+ { KEY_F24, Qt::Key_F24 },
+ //{ KEY_PROG3, Qt::Key_ },
+ //{ KEY_PROG4, Qt::Key_ },
+ //{ KEY_DASHBOARD, Qt::Key_ },
+ { KEY_SUSPEND, Qt::Key_Suspend },
+ //{ KEY_HP, Qt::Key_ },
+ //{ KEY_SOUND, Qt::Key_ },
+ { KEY_QUESTION, Qt::Key_Question },
+ //{ KEY_CONNECT, Qt::Key_ },
+ //{ KEY_SPORT, Qt::Key_ },
+ { KEY_SHOP, Qt::Key_Shop },
+ //{ KEY_ALTERASE, Qt::Key_ },
+ //{ KEY_SWITCHVIDEOMODE, Qt::Key_Mode_switch }
+ { KEY_BATTERY, Qt::Key_Battery },
+ { KEY_BLUETOOTH, Qt::Key_Bluetooth },
+ { KEY_WLAN, Qt::Key_WLAN },
+ { KEY_UWB, Qt::Key_UWB },
+ //{ KEY_WWAN_WIMAX, Qt::Key_ },
+ //{ KEY_RFKILL, Qt::Key_ },
+ //{ KEY_CHANNEL, Qt::Key_ },
+ { KEY_BTN_0, Qt::Key_0 },
+ { KEY_BTN_1, Qt::Key_1 },
+ { KEY_BTN_2, Qt::Key_2 },
+ { KEY_BTN_3, Qt::Key_3 },
+ { KEY_BTN_4, Qt::Key_4 },
+ { KEY_BTN_5, Qt::Key_5 },
+ { KEY_BTN_6, Qt::Key_6 },
+ { KEY_BTN_7, Qt::Key_7 },
+ { KEY_BTN_8, Qt::Key_8 },
+ { KEY_BTN_9, Qt::Key_9 }
+ };
+ auto result = sKeyMap.value(keyCode, Qt::Key_unknown);
+
+ /* 按下shift 按键映射 */
+ static QHash<int32_t, Qt::Key> sKeyShiftMap{
+ { KEY_GRAVE, Qt::Key_AsciiTilde},
+ { KEY_1, Qt::Key_Exclam },
+ { KEY_2, Qt::Key_At },
+ { KEY_3, Qt::Key_NumberSign },
+ { KEY_4, Qt::Key_Dollar },
+ { KEY_5, Qt::Key_Percent },
+ { KEY_6, Qt::Key_AsciiCircum },
+ { KEY_7, Qt::Key_Ampersand },
+ { KEY_8, Qt::Key_Asterisk },
+ { KEY_9, Qt::Key_ParenLeft },
+ { KEY_0, Qt::Key_ParenRight },
+ { KEY_MINUS, Qt::Key_Underscore},
+ { KEY_EQUALS, Qt::Key_Plus},
+ { KEY_LEFT_BRACKET, Qt::Key_BraceLeft},
+ { KEY_RIGHT_BRACKET, Qt::Key_BraceRight},
+ { KEY_SEMICOLON, Qt::Key_Colon},
+ { KEY_APOSTROPHE, Qt::Key_QuoteDbl},
+ { KEY_BACKSLASH, Qt::Key_Bar},
+ { KEY_COMMA, Qt::Key_Less},
+ { KEY_PERIOD, Qt::Key_Greater},
+ { KEY_SLASH, Qt::Key_Question}
+ };
+
+ /* 按下NUM_Lock按键映射 */
+ static QHash<int32_t, Qt::Key> sKeyNumLockMap{
+ { KEY_NUMPAD_0, Qt::Key_Insert },
+ { KEY_NUMPAD_1, Qt::Key_End },
+ { KEY_NUMPAD_2, Qt::Key_Down },
+ { KEY_NUMPAD_3, Qt::Key_PageDown },
+ { KEY_NUMPAD_4, Qt::Key_Left },
+ { KEY_NUMPAD_5, Qt::Key_Clear },
+ { KEY_NUMPAD_6, Qt::Key_Right },
+ { KEY_NUMPAD_7, Qt::Key_Home },
+ { KEY_NUMPAD_8, Qt::Key_Up },
+ { KEY_NUMPAD_9, Qt::Key_PageUp },
+ { KEY_NUMPAD_DOT, Qt::Key_Delete },
+ };
+
+
+ /* NUM_Lock未按下 */
+ if (!isKeyLockActive(KEY_NUM_LOCK) && sKeyNumLockMap.contains(keyCode)) {
+ result = sKeyNumLockMap.value(keyCode, Qt::Key_unknown);
+ }
+
+ /* Shift按下 */
+ if (modifiers.testFlag(Qt::ShiftModifier) && sKeyShiftMap.contains(keyCode)) {
+ result = sKeyShiftMap.value(keyCode, Qt::Key_unknown);
+ }
+ return result;
+}
+
+Qt::KeyboardModifier QOhKeys::ohKeyboardModifier2QtKeyboardModifier(int32_t keyCode)
+{
+ static QHash<int32_t, Qt::KeyboardModifier> sKeyModifers{
+ { KEY_SHIFT_LEFT, Qt::ShiftModifier },
+ { KEY_SHIFT_RIGHT, Qt::ShiftModifier },
+ { KEY_CTRL_LEFT, Qt::ControlModifier },
+ { KEY_CTRL_RIGHT, Qt::ControlModifier },
+ { KEY_ALT_LEFT, Qt::AltModifier },
+ { KEY_ALT_RIGHT, Qt::AltModifier },
+ { KEY_META_LEFT, Qt::MetaModifier },
+ { KEY_META_RIGHT, Qt::MetaModifier }
+ /*,
+ { KEY_NUM_LOCK, Qt::KeypadModifier },
+ { KEY_NUMPAD_0, Qt::KeypadModifier },
+ { KEY_NUMPAD_1, Qt::KeypadModifier },
+ { KEY_NUMPAD_2, Qt::KeypadModifier },
+ { KEY_NUMPAD_3, Qt::KeypadModifier },
+ { KEY_NUMPAD_4, Qt::KeypadModifier },
+ { KEY_NUMPAD_5, Qt::KeypadModifier },
+ { KEY_NUMPAD_6, Qt::KeypadModifier },
+ { KEY_NUMPAD_7, Qt::KeypadModifier },
+ { KEY_NUMPAD_8, Qt::KeypadModifier },
+ { KEY_NUMPAD_9, Qt::KeypadModifier },
+ { KEY_NUMPAD_DIVIDE, Qt::KeypadModifier },
+ { KEY_NUMPAD_MULTIPLY, Qt::KeypadModifier },
+ { KEY_NUMPAD_SUBTRACT, Qt::KeypadModifier },
+ { KEY_NUMPAD_ADD, Qt::KeypadModifier },
+ { KEY_NUMPAD_DOT, Qt::KeypadModifier },
+ { KEY_NUMPAD_COMMA, Qt::KeypadModifier },
+ { KEY_NUMPAD_ENTER, Qt::KeypadModifier },
+ { KEY_NUMPAD_EQUALS, Qt::KeypadModifier },
+ { KEY_NUMPAD_LEFT_PAREN, Qt::KeypadModifier },
+ { KEY_NUMPAD_RIGHT_PAREN, Qt::KeypadModifier},
+ */
+ };
+ return sKeyModifers.value(keyCode, Qt::NoModifier);
+}
+
+bool QOhKeys::autoRepeat(int32_t type, int32_t code)
+{
+ /* 长按判断
+ * 记录按下的字符,判断字符是否已经按下
+ * 如果字符已经按下则认为是长按状态
+ */
+ bool result = false;
+ // 0 for down
+ if (0 == type) {
+ KeyRecord *rec = key_recorder.findKey(int(code), false);
+ if (rec) {
+ result = true;
+ } else {
+ key_recorder.storeKey(int(code), 0, 0, QString());
+ }
+ } else {
+ key_recorder.findKey(int(code), true);
+ }
+ return result;
+}
+
+QString QOhKeys::key2String(Qt::Key key, Qt::KeyboardModifiers modifiers)
+{
+ static QHash<Qt::Key, QString> sKeyTextMap{
+ { Qt::Key_Home, QString() },
+ { Qt::Key_Back, QString() },
+ { Qt::Key_MediaTogglePlayPause, "MediaTogglePlayPause" },
+ { Qt::Key_MediaStop, "MediaStop" },
+ { Qt::Key_MediaNext, "MediaNext" },
+ { Qt::Key_MediaPrevious, "MediaPrevious" },
+ { Qt::Key_AudioRewind, "AudioRewind" },
+ { Qt::Key_AudioForward, "AudioForward" },
+ { Qt::Key_VolumeUp, "VolumeUp" },
+ { Qt::Key_VolumeDown, "VolumeDown" },
+ { Qt::Key_PowerDown, "PowerDown" },
+ { Qt::Key_Camera, "Camera" },
+ { Qt::Key_VolumeMute, "VolumeMute" },
+ { Qt::Key_MicMute, "MicMute" },
+ { Qt::Key_MonBrightnessUp, "MonBrightnessUp" },
+ { Qt::Key_MonBrightnessDown, "MonBrightnessDown" },
+ { Qt::Key_0, "0" },
+ { Qt::Key_1, "1" },
+ { Qt::Key_2, "2" },
+ { Qt::Key_3, "3" },
+ { Qt::Key_4, "4" },
+ { Qt::Key_5, "5" },
+ { Qt::Key_6, "6" },
+ { Qt::Key_7, "7" },
+ { Qt::Key_8, "8" },
+ { Qt::Key_9, "9" },
+ { Qt::Key_Asterisk, "Asterisk" },
+ { Qt::Key_NumberSign, "NumberSign" },
+ { Qt::Key_Up, QString() },
+ { Qt::Key_Down, QString() },
+ { Qt::Key_Left, QString() },
+ { Qt::Key_Right, QString() },
+ //{ Qt::Key_, QString() },
+ { Qt::Key_A, "A" },
+ { Qt::Key_B, "B" },
+ { Qt::Key_C, "C" },
+ { Qt::Key_D, "D" },
+ { Qt::Key_E, "E" },
+ { Qt::Key_F, "F" },
+ { Qt::Key_G, "G" },
+ { Qt::Key_H, "H" },
+ { Qt::Key_I, "I" },
+ { Qt::Key_J, "J" },
+ { Qt::Key_K, "K" },
+ { Qt::Key_L, "L" },
+ { Qt::Key_M, "M" },
+ { Qt::Key_N, "N" },
+ { Qt::Key_O, "O" },
+ { Qt::Key_P, "P" },
+ { Qt::Key_Q, "Q" },
+ { Qt::Key_R, "R" },
+ { Qt::Key_S, "S" },
+ { Qt::Key_T, "T" },
+ { Qt::Key_U, "U" },
+ { Qt::Key_V, "V" },
+ { Qt::Key_W, "W" },
+ { Qt::Key_X, "X" },
+ { Qt::Key_Y, "Y" },
+ { Qt::Key_Z, "Z" },
+ { Qt::Key_Comma, "," },
+ { Qt::Key_Period, "." },
+ { Qt::Key_Alt, QString() },
+ { Qt::Key_Shift, QString() },
+ { Qt::Key_Tab, "\t" },
+ { Qt::Key_Space, QString(" ") },
+ //{ Qt::KEY_, QString() },
+ { Qt::Key_Explorer, "Explorer" },
+ //{ Qt::Key_, QString() },
+ { Qt::Key_Enter, "\r" },
+ { Qt::Key_Backspace, "\b" },
+ { Qt::Key_QuoteLeft, "`" },
+ { Qt::Key_Apostrophe, "'" },
+ { Qt::Key_Minus, "-" },
+ { Qt::Key_Equal, "=" },
+ { Qt::Key_BracketLeft, "[" },
+ { Qt::Key_BracketRight, "]" },
+ { Qt::Key_Backslash, "\\" },
+ { Qt::Key_Semicolon, ";" },
+ { Qt::Key_Slash, "/" },
+ { Qt::Key_At, "At" },
+ { Qt::Key_Plus, "+" },
+ { Qt::Key_Menu, "Menu" },
+ { Qt::Key_PageUp, QString() },
+ { Qt::Key_PageDown, QString() },
+ { Qt::Key_Escape, QString() },
+ { Qt::Key_Delete, QString() },
+ { Qt::Key_Control, QString() },
+ { Qt::Key_CapsLock, QString() },
+ { Qt::Key_ScrollLock, "ScrollLock" },
+ { Qt::Key_Meta, QString() },
+ { Qt::Key_SysReq, "SysReq" },
+ { Qt::Key_Pause, "Pause" },
+ { Qt::Key_End, QString() },
+ { Qt::Key_Insert, QString() },
+ { Qt::Key_Forward, "Forward" },
+ { Qt::Key_MediaPlay, "MediaPlay" },
+ { Qt::Key_MediaPause, "MediaPause" },
+ { Qt::Key_MediaRecord, "MediaRecord," },
+ { Qt::Key_F1, QString() },
+ { Qt::Key_F2, QString() },
+ { Qt::Key_F3, QString() },
+ { Qt::Key_F4, QString() },
+ { Qt::Key_F5, QString() },
+ { Qt::Key_F6, QString() },
+ { Qt::Key_F7, QString() },
+ { Qt::Key_F8, QString() },
+ { Qt::Key_F9, QString() },
+ { Qt::Key_F10, QString() },
+ { Qt::Key_F11, QString() },
+ { Qt::Key_F12, QString() },
+ { Qt::Key_NumLock, QString() },
+ { Qt::Key_ParenLeft, QString() },
+ { Qt::Key_ParenRight, QString() },
+ { Qt::Key_Sleep, "Sleep" },
+ { Qt::Key_Zenkaku_Hankaku, "Zenkaku_Hankaku" },
+ { Qt::Key_Katakana, "Katakana," },
+ { Qt::Key_Hiragana, "Hiragana" },
+ { Qt::Key_Henkan, "Henkan" },
+ { Qt::Key_Hiragana_Katakana, "Hiragana_Katakana" },
+ { Qt::Key_Muhenkan, "Muhenkan" },
+ { Qt::Key_Return, "\r" },
+ { Qt::Key_Hangul, "Hangul" },
+ { Qt::Key_Hangul_Hanja, "Hangul_Hanja" },
+ { Qt::Key_yen, "yen" },
+ { Qt::Key_Stop, "Stop" },
+ { Qt::Key_Undo, "Undo" },
+ { Qt::Key_Copy, "Copy" },
+ { Qt::Key_Open, "Open" },
+ { Qt::Key_Paste, "Paste" },
+ { Qt::Key_Find, "Find" },
+ { Qt::Key_Cut, "Cut" },
+ { Qt::Key_Help, "Help" },
+ { Qt::Key_Refresh, "Refresh" },
+ { Qt::Key_Exit, "Exit" },
+ { Qt::Key_New, "New" },
+ { Qt::Key_Redo, "Redo" },
+ { Qt::Key_Close, "Close" },
+ { Qt::Key_Play, "Play" },
+ { Qt::Key_Print, "Print" },
+ { Qt::Key_Finance, "Finance" },
+ { Qt::Key_Cancel, "Cancel" },
+ { Qt::Key_KeyboardBrightnessDown, "KeyboardBrightnessDown" },
+ { Qt::Key_KeyboardBrightnessUp, "KeyboardBrightnessUp" },
+ { Qt::Key_Send, "Send" },
+ { Qt::Key_Reply, "Reply" },
+ { Qt::Key_Save, "Save" },
+ { Qt::Key_Documents, "Documents" },
+ { Qt::Key_Go, "Go" },
+ { Qt::Key_Info, "Info" },
+ { Qt::Key_Subtitle, "Subtitle" },
+ { Qt::Key_CD, "CD" },
+ { Qt::Key_Video, "Video" },
+ { Qt::Key_Memo, "Memo" },
+ { Qt::Key_Calendar, "Calendar" },
+ { Qt::Key_Red, "Red" },
+ { Qt::Key_Green, "Green" },
+ { Qt::Key_Yellow, "Yellow" },
+ { Qt::Key_Blue, "Blue" },
+ { Qt::Key_ChannelUp, "ChannelUp" },
+ { Qt::Key_ChannelDown, "ChannelDown" },
+ { Qt::Key_Game, "Game" },
+ { Qt::Key_ZoomIn, "ZoomIn" },
+ { Qt::Key_ZoomOut, "ZoomOut" },
+ { Qt::Key_News, "News" },
+ { Qt::Key_Messenger, "Messenger" },
+ { Qt::Key_ScreenSaver, "ScreenSaver" },
+ { Qt::Key_WakeUp, "WakeUp" },
+ { Qt::Key_Xfer, "Xfer" },
+ { Qt::Key_Eject, "Eject" },
+ { Qt::Key_F13, "F13" },
+ { Qt::Key_F14, "F14" },
+ { Qt::Key_F15, "F15" },
+ { Qt::Key_F16, "F16" },
+ { Qt::Key_F17, "F17" },
+ { Qt::Key_F18, "F18" },
+ { Qt::Key_F19, "F19" },
+ { Qt::Key_F20, "F20" },
+ { Qt::Key_F21, "F21" },
+ { Qt::Key_F22, "F22" },
+ { Qt::Key_F23, "F23" },
+ { Qt::Key_F24, "F24" },
+ { Qt::Key_Suspend, "Suspend" },
+ { Qt::Key_Question, "Question" },
+ { Qt::Key_Shop, "Shop" },
+ { Qt::Key_Battery, "Battery" },
+ { Qt::Key_Bluetooth, "Bluetooth" },
+ { Qt::Key_WLAN, "WLAN" },
+ { Qt::Key_UWB, "UWB" },
+ { Qt::Key_AsciiTilde, "~"},
+ { Qt::Key_Exclam, "!" },
+ { Qt::Key_At, "@" },
+ { Qt::Key_NumberSign, "#" },
+ { Qt::Key_Dollar, "$" },
+ { Qt::Key_Percent, "%" },
+ { Qt::Key_AsciiCircum, "^" },
+ { Qt::Key_Ampersand, "&" },
+ { Qt::Key_Asterisk, "*" },
+ { Qt::Key_ParenLeft, "(" },
+ { Qt::Key_ParenRight, ")" },
+ { Qt::Key_Underscore, "_" },
+ { Qt::Key_Plus, "+" },
+ { Qt::Key_BraceLeft, "{"},
+ { Qt::Key_BraceRight, "}"},
+ { Qt::Key_Colon, ":" },
+ { Qt::Key_QuoteDbl, R"(")" },
+ { Qt::Key_Bar, "|" },
+ { Qt::Key_Less, "<" },
+ { Qt::Key_Greater, ">" },
+ { Qt::Key_Question, "?" },
+ { Qt::Key_Clear, QString()}
+ };
+ auto result = QString();
+ /* Ctril按下*/
+ if (modifiers.testFlag(Qt::ControlModifier) && (key == Qt::Key_Return
+ || key == Qt::Key_Enter)) {
+ result = "\n";
+ } else {
+ result = sKeyTextMap.value(key, QString());
+ bool bPressedShift = modifiers.testFlag(Qt::ShiftModifier);
+ /* 字符键盘 && 小写 */
+ if (Qt::Key_A <= key && (Qt::Key_Z >= key)) {
+ /* KEY_CAPS_LOCK打开 - KEY_SHIFT_LEFT按下 || 都没打开 */
+ if ((isKeyLockActive(KEY_CAPS_LOCK) && bPressedShift) ||
+ (!isKeyLockActive(KEY_CAPS_LOCK) && (!bPressedShift))) {
+ /* 小写 */
+ result = result.toLower();
+ }
+ }
+ }
+ return result;
+}
+
+Qt::KeyboardModifiers QOhKeys::keyboardModifiers()
+{
+ Qt::KeyboardModifiers mods(Qt::NoModifier);
+
+ if (isKeyPressed(KEY_SHIFT_LEFT) || isKeyPressed(KEY_SHIFT_RIGHT)) {
+ mods |= Qt::ShiftModifier;
+ }
+
+ if (isKeyPressed(KEY_CTRL_LEFT) || isKeyPressed(KEY_CTRL_RIGHT)) {
+ mods |= Qt::ControlModifier;
+ }
+
+ if (isKeyPressed(KEY_ALT_LEFT) || isKeyPressed(KEY_ALT_RIGHT)) {
+ mods |= Qt::AltModifier;
+ }
+
+ if (isKeyPressed(KEY_META_LEFT) || isKeyPressed(KEY_META_RIGHT)) {
+ mods |= Qt::MetaModifier;
+ }
+ return mods;
+}
+
+Qt::KeyboardModifiers QOhKeys::metaModifier()
+{
+ Qt::KeyboardModifiers mods(Qt::NoModifier);
+ if (isKeyPressed(KEY_META_LEFT) || isKeyPressed(KEY_META_RIGHT)) {
+ mods |= Qt::MetaModifier;
+ }
+ return mods;
+}
+
+Qt::KeyboardModifiers QOhKeys::ohKeys2Qt(int64_t keys)
+{
+ Qt::KeyboardModifiers km = Qt::NoModifier;
+ if (keys & ARKUI_MODIFIER_KEY_CTRL) {
+ km |= Qt::ControlModifier;
+ }
+ if (keys & ARKUI_MODIFIER_KEY_SHIFT) {
+ km |= Qt::ShiftModifier;
+ }
+ if (keys & ARKUI_MODIFIER_KEY_ALT) {
+ km |= Qt::AltModifier;
+ }
+ return km;
+}
+
+bool QOhKeys::isKeypad(int32_t keyCode)
+{
+ return keyCode >= KEY_NUM_LOCK && keyCode <= KEY_NUMPAD_RIGHT_PAREN;
+}
+
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,38 @@
+#ifndef QOHKEYS_H
+#define QOHKEYS_H
+
+#include <ace/xcomponent/native_xcomponent_key_event.h>
+#include <multimodalinput/oh_input_manager.h>
+
+#include <qnamespace.h>
+#include "qohevent.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOhKeys
+{
+public:
+ static bool isKeyPressed(int32_t keyCode);
+
+ static bool isKeyLockActive(int32_t keyCode);
+
+ static Qt::Key ohCode2QtKey(int32_t keyCode, Qt::KeyboardModifiers modifiers);
+
+ static Qt::KeyboardModifier ohKeyboardModifier2QtKeyboardModifier(int32_t keyCode);
+
+ static bool autoRepeat(int32_t type, int32_t code);
+
+ static QString key2String(Qt::Key key, Qt::KeyboardModifiers modifiers);
+
+ static Qt::KeyboardModifiers keyboardModifiers();
+
+ static Qt::KeyboardModifiers metaModifier();
+
+ static Qt::KeyboardModifiers ohKeys2Qt(int64_t keys);
+
+ static bool isKeypad(int32_t keyCode);
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHKEYS_H
new file mode 100644
@@ -0,0 +1,227 @@
+#include <dlfcn.h>
+#include <QString>
+#include <QDir>
+#include <qnapi.h>
+#include <qos/qos.h>
+#include <QByteArray>
+#include <qopenharmony.h>
+#include <QVarLengthArray> /* Qt 6 only modify */
+#include <napi/native_api.h>
+#include <private/qopenharmony_p.h>
+#include <QtCore/qopenharmonydefines.h>
+#include <qpa/qwindowsysteminterface_p.h>
+
+
+#include "qohmain.h"
+#include "qjswant.h"
+#include "qjscontext.h"
+#include "qjsability.h"
+#include "qohauxiliary.h"
+#include "qjsabilitystage.h"
+#include "qjsabilityfactory.h"
+#include "qohsystemtrayicon.h"
+#include "qohnativewindowmanager.h"
+
+Q_LOGGING_CATEGORY(openharmonyQPA, "openharmony.qpa");
+
+extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
+pthread_t m_qtAppThread = 0;
+static QList<QByteArray> m_applicationParams;
+
+static void *startMainMethod(void *arg)
+{
+ Q_UNUSED(arg);
+ OH_QoS_SetThreadQoS(QOS_USER_INTERACTIVE);
+ QoS_Level level;
+ OH_QoS_GetThreadQoS(&level);
+ if (level != QOS_USER_INTERACTIVE)
+ LOGW("set qt main thread level failed");
+
+ QByteArray fileName = m_applicationParams.first();
+ LOGI("load qt application %{public}s", fileName.constData());
+
+ //look for main()
+ // Obtain a handle to the main library (the library that contains the main() function).
+ void *mainLibraryHnd = dlopen(fileName.constData(), 0);
+ if (Q_UNLIKELY(!mainLibraryHnd)) {
+ QString error = QString("Failed to load QT application '%1': %2")
+ .arg(fileName.constData())
+ .arg(dlerror());
+ LOGE("%{public}s", error.toUtf8().constData());
+ return nullptr;
+ }
+ static Main mainMethod = nullptr;
+ mainMethod = (Main)dlsym(mainLibraryHnd, "main");
+ if (Q_UNLIKELY(!mainMethod)) {
+ QString error = QString("Failed to find main function: %1").arg(dlerror());
+ LOGE("%{public}s", error.toUtf8().constData());
+ return nullptr;
+ }
+
+ QVarLengthArray<const char *> params(m_applicationParams.size());
+
+ for (int i = 0; i < m_applicationParams.size(); i++)
+ params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+
+ int ret = mainMethod(m_applicationParams.length(), const_cast<char **>(params.data()));
+ Q_UNUSED(ret);
+ if (mainLibraryHnd) {
+ int res = dlclose(mainLibraryHnd);
+ if (res < 0)
+ LOGE("dlclose failed: %{public}s", dlerror());
+ }
+
+ //非gui程序退出
+ qNativeWindowManager->destoryAllAbility();
+ return nullptr;
+}
+
+static bool parseModule(const Napi::CallbackInfo &info)
+{
+ if (info.Length() < 1)
+ return false;
+ auto ability = QNapi::getFirst<Napi::Object>(info);
+ if (ability.IsEmpty() || ability.IsUndefined())
+ return false;
+ auto want = QNapi::get<Napi::Object>(ability, "launchWant");
+ if (want.IsEmpty() || want.IsUndefined())
+ return false;
+ auto bundleName = QNapi::get<QString>(want, "bundleName");
+ auto moduleName = QNapi::get<QString>(want, "moduleName");
+ auto abilityName = QNapi::get<QString>(want, "abilityName");
+ QtOh::setBundleName(bundleName);
+ QtOh::setModuleName(moduleName);
+ QtOh::setAbilityName(abilityName);
+ return true;
+}
+
+static Napi::Value startQtApplication(const Napi::CallbackInfo& info)
+{
+ static bool firstStart = true;
+ if (firstStart) {
+ if (!parseModule(info))
+ return QNapi::create(false);
+ }
+ QJsAbility *ability = QJsAbilityFactory::create(info);
+ if (ability == nullptr)
+ return QNapi::create(false);
+ qNativeWindowManager->handleTopWindowCreated(ability);
+ if (!firstStart) {
+ return QNapi::create(true);
+ }
+ firstStart = false;
+
+ QJsContext *context = ability->context();
+ if (context == nullptr) {
+ LOGE("context is null");
+ return QNapi::create(false);
+ }
+ QByteArray bundle = context->bundleCodeDir().toLatin1();
+ if (bundle.isEmpty()) {
+ LOGE("args is vaild");
+ return QNapi::create(false);
+ }
+
+#if defined(Q_PROCESSOR_ARM_64)
+ QByteArray libDir = bundle + "/libs/arm64";
+#elif defined(Q_PROCESSOR_ARM_32)
+ QByteArray libDir = bundle + "/libs/arm";
+#elif defined(Q_PROCESSOR_X86_64)
+ QByteArray libDir = bundle + "/libs/x86_64";
+#elif defined(Q_PROCESSOR_X86)
+ QByteArray libDir = bundle + "/libs/x86";
+#endif
+ QDir::setCurrent(libDir);
+ QByteArrayList qmlPluginsDirs = { libDir + "/qml" };
+ QByteArrayList qmls = { bundle + "/" + ability->launchWant()->moduleName().toLocal8Bit() + "/resources/rawfile" };
+ QString qmlfilesDir = context->qmlDir();
+ if (!qmlfilesDir.isEmpty())
+ qmls << qmlfilesDir.toLocal8Bit();
+
+ // Helper function to set environment variables
+ auto setEnv = [](const char *name, const QByteArray &value) {
+ if (::setenv(name, value.constData(), 1) != 0) {
+ LOGE("Can't set environment for %{public}s", name);
+ }
+ };
+
+ setEnv("QT_HARMONY_QML_FILE_DIR", qPrintable(qmlfilesDir));
+ setEnv("QT_QPA_PLATFORM_PLUGIN_PATH", libDir);
+ //setEnv("QML_DISABLE_DISK_CACHE", "1");
+ setEnv("QT_PLUGIN_PATH", libDir);
+ setEnv("QT_HARMONY_QML_PLUGINS_PATH", qmlPluginsDirs.join(":"));
+ setEnv("QML2_IMPORT_PATH", qmls.join(":"));
+#if 0
+ setEnv("QT_DEBUG_PLUGINS", "1");
+#endif
+ qputenv("TMPDIR", context->applicationContext()->tempDir().toLatin1());
+ ability->launchWant()->saveParamsToEnv();
+
+ m_applicationParams = QList<QByteArray>() << ability->launchQtApplication();
+ QList<QByteArray> params = ability->launchQtParams();
+ params.removeAll(" ");
+ params.removeAll("");
+ m_applicationParams << params;
+
+ if (m_applicationParams.empty()) {
+ LOGE("No QT application specified");
+ return QNapi::create(false);
+ }
+ QByteArray fileName = m_applicationParams.first();
+ m_applicationParams[0] = libDir + "/" + fileName;
+
+ QJsContext *applicationContext = context->applicationContext();
+ QtOh::setDir("DIRECTORY_CACHE", applicationContext->cacheDir());
+ QtOh::setDir("DIRECTORY_TEMP", applicationContext->tempDir());
+ QtOh::setDir("DIRECTORY_BUNDLE", applicationContext->bundleCodeDir());
+ QtOh::setDir("DIRECTORY_FILES", applicationContext->filesDir());
+ QtOh::setDir("DIRECTORY_RESOURCE", context->resourceDir());
+ QtOh::setDir("DIRECTORY_HAP_FILES", context->filesDir());
+
+ pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr);
+
+ return QNapi::create(true);
+}
+
+static Napi::Value handleOnNewWant(const Napi::CallbackInfo& info)
+{
+ if (info.Length() < 2)
+ return QNapi::create(false);
+
+ QList<QByteArray> fileUris;
+ Napi::Object jsWant = QNapi::getFirst<Napi::Object>(info);
+ QJsWant want(jsWant);
+ fileUris << want.launchParams();
+ for (QByteArray fileName : fileUris) {
+ if (fileName.isEmpty())
+ continue;
+ QString path = QString::fromUtf8(fileName);
+ if (!path.isEmpty()) {
+ QUrl result = QUrl::fromLocalFile(path);
+ QtOh::runOnQtMainThread([](const QUrl &url){
+ QWindowSystemInterface::handleFileOpenEvent(url);
+ }, result);
+ }
+ }
+
+ return QNapi::create(true);
+}
+
+Napi::Object Init(Napi::Env env, Napi::Object exports) {
+ static bool inited = false;
+ if (!inited) {
+ QtOh::setUIEnv(env);
+ QJsAbilityStage::init(env, exports);
+ QOhSystemTrayIcon::init(env, exports);
+ QNapi::set(exports, "handleOnNewWant", handleOnNewWant);
+ QNapi::set(exports, "startQtApplication", startQtApplication);
+ /* NOTE QGuiApplication根据tabletEvent合成鼠标事件 */
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ inited = true;
+ }
+
+ QOhNativeWindowManager::init(env, exports);
+ return exports;
+}
+
+NODE_API_MODULE(plugins_platforms_qopenharmony, Init)
new file mode 100644
@@ -0,0 +1,25 @@
+#ifndef QOHMAIN_H
+#define QOHMAIN_H
+
+#include <QLoggingCategory>
+#include <arkui/native_node.h>
+QT_BEGIN_NAMESPACE
+
+struct Node {
+ ArkUI_NodeType nodeType; // Type of the node. The value can be ARKUI_NODE_XCOMPONENT or other types supported in later versions.
+ ArkUI_NodeHandle node; // ARKUI_NODE_XCOMPONENT type or other.
+ ArkUI_NodeHandle container; // Must be of type ARKUI_NODE_STACK.
+ char nodeOwner[8]; // Describes the body of the Node structure to be created, such as "QT," "CEF," "SDL2," "FLUT".
+ void* nodePrivate; // Structure used for the definition of the nodeOwner body record itself.
+};
+
+struct WindowNode : Node
+{
+ void *window;
+};
+
+Q_DECLARE_LOGGING_CATEGORY(openharmonyQPA)
+
+QT_END_NAMESPACE
+
+#endif //QOHMAIN_H
new file mode 100644
@@ -0,0 +1,115 @@
+#include "qohnativenodeapi.h"
+#include <arkui/native_interface.h>
+
+QT_BEGIN_NAMESPACE
+
+ArkUI_NativeNodeAPI_1 *QOhNativeNodeAPI::m_nativeNodeAPI = []{
+ return reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
+ OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
+}();
+
+ArkUI_NativeNodeAPI_1 *QOhNativeNodeAPI::nativeNodeAPI()
+{
+ return m_nativeNodeAPI;
+}
+
+QSharedPointer<QOhNativeNodeAPI> QOhNativeNodeAPI::getOrCreateForNode(QOhWindowNode *node)
+{
+ if (node == nullptr)
+ return nullptr;
+ if (m_apiForNode.contains(node))
+ return m_apiForNode.value(node);
+ QSharedPointer<QOhNativeNodeAPI> api(new QOhNativeNodeAPI());
+ m_apiForNode.insert(node, api);
+ return api;
+}
+
+ArkUI_NodeHandle QOhNativeNodeAPI::createNode(ArkUI_NodeType type)
+{
+ if (m_nativeNodeAPI->createNode == nullptr)
+ return nullptr;
+ if (type == ARKUI_NODE_XCOMPONENT) {
+ m_xcomponent = m_nativeNodeAPI->createNode(type);
+ return m_xcomponent;
+ } else if (type == ARKUI_NODE_STACK) {
+ m_stack = m_nativeNodeAPI->createNode(type);
+ return m_stack;
+ }
+ return nullptr;
+}
+
+QOhNativeNodeAPI::QOhNativeNodeAPI()
+ : m_stack(nullptr)
+ , m_xcomponent(nullptr)
+{
+
+}
+
+bool QOhNativeNodeAPI::setAttribute(ArkUI_NodeHandle node, ArkUI_NodeAttributeType attribute, const ArkUI_AttributeItem *item)
+{
+ if (!has(&ArkUI_NativeNodeAPI_1::setAttribute)) {
+ LOGE("node napi setAttribute nullptr error");
+ return false;
+ }
+ LOGI("QOhNativeNodeAPI::setAttribute %{public}s", QtOhArkUI::nodeAttributeTypeToString(attribute).data());
+ auto code = m_nativeNodeAPI->setAttribute(node, attribute, item);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode(QString("QOhNativeNodeAPI::setAttribute %1 failed:").arg(QtOhArkUI::nodeAttributeTypeToString(attribute).data()), code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhNativeNodeAPI::addChild(ArkUI_NodeHandle parent, ArkUI_NodeHandle child)
+{
+ if (!has(&ArkUI_NativeNodeAPI_1::addChild)) {
+ LOGE("node napi addChild nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->addChild(parent, child);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("addChild failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+bool QOhNativeNodeAPI::removeChild(ArkUI_NodeHandle parent, ArkUI_NodeHandle child)
+{
+ if (!has(&ArkUI_NativeNodeAPI_1::removeChild)) {
+ LOGE("node napi removeChild nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->removeChild(parent, child);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("removeChild failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+ArkUI_NodeHandle QOhNativeNodeAPI::parent(ArkUI_NodeHandle handle)
+{
+ if (!has(&ArkUI_NativeNodeAPI_1::getParent)) {
+ LOGE("node napi getParent nullptr error");
+ return nullptr;
+ }
+ return m_nativeNodeAPI->getParent(handle);
+}
+
+bool QOhNativeNodeAPI::setLengthMetricUnit(ArkUI_NodeHandle handle, ArkUI_LengthMetricUnit unit)
+{
+ if (!has(&ArkUI_NativeNodeAPI_1::setLengthMetricUnit)) {
+ LOGE("node napi setLengthMetricUnit nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->setLengthMetricUnit(m_stack, unit);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("setLengthMetricUnit failed: ", code);
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,241 @@
+#ifndef QOHNATIVENODEAPI_H
+#define QOHNATIVENODEAPI_H
+
+#include <QHash>
+#include <arkui/native_node.h>
+#include <cstdint>
+#include <utility>
+#include <type_traits>
+#include <qopenharmonydefines.h>
+#include <QSharedPointer>
+
+#include "qoharkui.h"
+
+QT_BEGIN_NAMESPACE
+class QOhWindowNode;
+
+template<typename... Args>
+ArkUI_NumberValue* createValue(Args&&... args) {
+ ArkUI_NumberValue* values = new ArkUI_NumberValue[sizeof...(Args)];
+ size_t index = 0;
+
+ auto initValue = [&](auto&& arg) {
+ using ArgType = std::decay_t<decltype(arg)>;
+
+ if constexpr (std::is_same_v<ArgType, float>) {
+ values[index].f32 = static_cast<float>(arg);
+ } else if constexpr (std::is_same_v<ArgType, int32_t>) {
+ values[index].i32 = static_cast<int32_t>(arg);
+ } else if constexpr (std::is_same_v<ArgType, uint32_t>) {
+ values[index].u32 = static_cast<uint32_t>(arg);
+ } else if constexpr (std::is_enum_v<ArgType>) {
+ values[index].i32 = static_cast<std::underlying_type_t<ArgType>>(arg);
+ } else if constexpr (std::is_same_v<ArgType, bool>) {
+ values[index].i32 = arg ? 1 : 0;
+ } else if constexpr (std::is_same_v<ArgType, double>) {
+ values[index].f32 = static_cast<float>(arg);
+ } else {
+ values[index].i32 = static_cast<int32_t>(arg);
+ }
+ index++;
+ };
+
+ (initValue(std::forward<Args>(args)), ...);
+ return values;
+}
+
+struct OhArkUIAttributeItem
+{
+ OhArkUIAttributeItem(ArkUI_AttributeItem item): _item(item) {}
+ ~OhArkUIAttributeItem() { if ( _item.value != nullptr) delete[] _item.value; }
+ ArkUI_AttributeItem _item;
+};
+
+template<typename... Args>
+OhArkUIAttributeItem createAttributeItem(Args&&... args) {
+ return OhArkUIAttributeItem(ArkUI_AttributeItem {
+ .value = createValue(std::forward<Args>(args)...),
+ .size = static_cast<int32_t>(sizeof...(Args)),
+ .string = nullptr,
+ .object = nullptr
+ });
+}
+
+inline OhArkUIAttributeItem createAttributeItem(const char* str) {
+ return OhArkUIAttributeItem(ArkUI_AttributeItem {
+ .value = nullptr,
+ .size = 0,
+ .string = str,
+ .object = nullptr
+ });
+}
+
+template<typename T>
+OhArkUIAttributeItem createAttributeItem(T* obj) {
+ return OhArkUIAttributeItem(ArkUI_AttributeItem {
+ .value = nullptr,
+ .size = 0,
+ .string = nullptr,
+ .object = static_cast<void*>(obj)
+ });
+}
+
+class QOhNativeNodeAPI
+{
+public:
+ struct XCompoentType {};
+ struct StackType {};
+ static ArkUI_NativeNodeAPI_1 *nativeNodeAPI();
+ static QSharedPointer<QOhNativeNodeAPI> getOrCreateForNode(QOhWindowNode *node);
+
+ template<typename Type = StackType>
+ void attach(ArkUI_NodeHandle handle) {
+ if constexpr (std::is_same_v<Type, XCompoentType>) {
+ m_xcomponent = handle;
+ return;
+ }
+ m_stack = handle;
+ }
+
+ template<typename Type = StackType>
+ static QOhNativeNodeAPI *createForHandle(ArkUI_NodeHandle handle) {
+ QOhNativeNodeAPI *api = new QOhNativeNodeAPI;
+ if constexpr (std::is_same_v<Type, XCompoentType>) {
+ api->m_xcomponent = handle;
+ return api;
+ }
+ api->m_stack = handle;
+ return api;
+ }
+
+ ArkUI_NodeHandle createNode(ArkUI_NodeType type);
+
+ template<typename Type = StackType, typename... Args>
+ bool setAttribute(ArkUI_NodeAttributeType attribute, Args&&... args)
+ {
+ OhArkUIAttributeItem item = createAttributeItem(args...);
+ return setAttribute(handle<Type>(), attribute, &item._item);
+ }
+
+ template<typename Type = StackType>
+ const ArkUI_AttributeItem *getAttribute(ArkUI_NodeAttributeType attribute)
+ {
+ if (!has(&ArkUI_NativeNodeAPI_1::getAttribute)) {
+ LOGE("node napi getAttribute nullptr error");
+ return nullptr;
+ }
+ return m_nativeNodeAPI->getAttribute(handle<Type>(), attribute);
+ }
+
+ bool addChild(ArkUI_NodeHandle parent, ArkUI_NodeHandle child);
+ bool removeChild(ArkUI_NodeHandle parent, ArkUI_NodeHandle child);
+
+ template<typename Type = StackType>
+ bool registerNodeEvent(ArkUI_NodeEventType eventType,
+ int32_t targetId, void* userData) {
+ if (!has(&ArkUI_NativeNodeAPI_1::registerNodeEvent)) {
+ LOGE("node napi registerNodeEvent nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->registerNodeEvent(handle<Type>(), eventType, targetId, userData);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("registerNodeEvent failed: ", code);
+ return false;
+ }
+ return true;
+ }
+
+ template<typename Type = StackType>
+ bool addNodeEventReceiver(void (*eventReceiver)(ArkUI_NodeEvent* event)) {
+ if (!has(&ArkUI_NativeNodeAPI_1::addNodeEventReceiver)) {
+ LOGE("node napi addNodeEventReceiver nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->addNodeEventReceiver(handle<Type>(), eventReceiver);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("addNodeEventReceiver failed: ", code);
+ return false;
+ }
+ return true;
+ }
+
+ template<typename Type = StackType>
+ void unregisterNodeEvent(ArkUI_NodeEventType eventType) {
+ if (!has(&ArkUI_NativeNodeAPI_1::unregisterNodeEvent)) {
+ LOGE("node napi unregisterNodeEvent nullptr error");
+ return;
+ }
+ m_nativeNodeAPI->unregisterNodeEvent(handle<Type>(), eventType);
+ }
+
+ template<typename Type = StackType>
+ bool removeNodeEventReceiver(void (*eventReceiver)(ArkUI_NodeEvent* event)) {
+ if (!has(&ArkUI_NativeNodeAPI_1::removeNodeEventReceiver)) {
+ LOGE("node napi removeNodeEventReceiver nullptr error");
+ return false;
+ }
+ auto code = m_nativeNodeAPI->removeNodeEventReceiver(handle<Type>(), eventReceiver);
+ if (!QtOhArkUI::ErrorCode::checkCode(code)) {
+ QtOhArkUI::ErrorCode::printErrorCode("removeNodeEventReceiver failed: ", code);
+ return false;
+ }
+ return true;
+ }
+
+ template<typename Type = StackType>
+ uint32_t totalChildCount() {
+ if (!has(&ArkUI_NativeNodeAPI_1::getTotalChildCount)) {
+ LOGE("node napi getTotalChildCount nullptr error");
+ return 0;
+ }
+ return m_nativeNodeAPI->getTotalChildCount(handle<Type>());
+ }
+
+ template<typename Type = StackType>
+ ArkUI_NodeHandle childAt(int32_t position) {
+ if (!has(&ArkUI_NativeNodeAPI_1::getChildAt)) {
+ LOGE("node napi getChildAt nullptr error");
+ return nullptr;
+ }
+ return m_nativeNodeAPI->getChildAt(handle<Type>(), position);
+ }
+
+ ArkUI_NodeHandle parent(ArkUI_NodeHandle handle);
+
+ template <typename T, typename Member>
+ static bool has(Member T::*member) {
+ return (m_nativeNodeAPI != nullptr) && ((m_nativeNodeAPI->*member) != nullptr);
+ }
+
+ template<typename Type = StackType>
+ void disposeNode() {
+ m_nativeNodeAPI->disposeNode(handle<Type>());
+ }
+
+ template<typename Type = StackType>
+ bool setLengthMetricUnit(ArkUI_LengthMetricUnit unit) {
+ return setLengthMetricUnit(handle<Type>(), unit);
+ }
+ bool setLengthMetricUnit(ArkUI_NodeHandle handle, ArkUI_LengthMetricUnit unit);
+
+private:
+ QOhNativeNodeAPI();
+ bool setAttribute(ArkUI_NodeHandle node, ArkUI_NodeAttributeType attribute, const ArkUI_AttributeItem* item);
+ template<typename Type>
+ ArkUI_NodeHandle handle() {
+ ArkUI_NodeHandle target = m_stack;
+ if constexpr (std::is_same_v<Type, XCompoentType>) {
+ target = m_xcomponent;
+ }
+ return target;
+ }
+private:
+ ArkUI_NodeHandle m_stack = nullptr;
+ ArkUI_NodeHandle m_xcomponent = nullptr;
+ static ArkUI_NativeNodeAPI_1 *m_nativeNodeAPI;
+ static inline QHash<QOhWindowNode *, QSharedPointer<QOhNativeNodeAPI>> m_apiForNode = {};
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHNATIVENODEAPI_H
new file mode 100644
@@ -0,0 +1,136 @@
+#include "qohnativevsync.h"
+#include "qohmain.h"
+#include "qohauxiliary.h"
+#include <native_window/external_window.h>
+#include <qopenharmony.h>
+
+#include <QWindow>
+#include <qpa/qplatformbackingstore.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhNativeVSync::QOhNativeVSync(QWindow *window, QPlatformBackingStore *backingStore)
+ : m_window(window)
+ , m_backingStore(backingStore)
+{
+ if (m_window.isNull())
+ return;
+ WId id = window->winId();
+ WindowNode *node = reinterpret_cast<WindowNode *>(id);
+
+ if (node == nullptr) {
+ return;
+ }
+
+ QtOh::runOnJsUIThreadAndWait([this, node]{
+ OHNativeWindow *nativeWindow = reinterpret_cast<OHNativeWindow *>(node->window);
+ if (nativeWindow == nullptr) {
+ return;
+ }
+ uint64_t surfaceId = 0;
+ int32_t error = OH_NativeWindow_GetSurfaceId(nativeWindow, &surfaceId);
+ if (error != 0)
+ return;
+
+ // OH_NativeVSync_Create_ForAssociatedWindow 系统bug, 参数名称中包含"_WM"时会导致电脑重启进入锁屏状态, Qt规避
+ // std::string name_ = window->objectName().toStdString();
+ static int index = 0;
+ std::string name_ = QString("qt_ohos_vsync_%1").arg(index++).toStdString();
+ m_vsync.reset(new QOhObjectHolder<OH_NativeVSync>(OH_NativeVSync_Create_ForAssociatedWindow,
+ OH_NativeVSync_Destroy, surfaceId, name_.data(), name_.length()));
+ if (Q_UNLIKELY(m_vsync->object() == nullptr)) {
+ qWarning() << "create vsync failed.";
+ return;
+ }
+ OH_NativeVSync_RequestFrame(m_vsync->object(), nativeVSyncFrameCallback, this);
+ });
+}
+
+void QOhNativeVSync::saveFrame(QWindow *wnd, const QRegion ®ion, const QPoint &offset)
+{
+ if (m_window.data() == wnd) {
+ m_pendingFlush = true;
+ m_region = m_region.united(region);
+ if (m_offset.has_value()) {
+ const QPoint &old = m_offset.value();
+ m_offset = QPoint(qMin(old.x(), offset.x()), qMin(old.y(), offset.y()));
+ } else {
+ m_offset = offset;
+ }
+ }
+}
+
+bool QOhNativeVSync::isValid() const
+{
+ return m_vsync->object() != nullptr;
+}
+
+bool QOhNativeVSync::isReady() const
+{
+ return m_ready.load();
+}
+
+void QOhNativeVSync::clearFlag()
+{
+ m_ready.store(false);
+}
+
+bool QOhNativeVSync::isPendingFlush() const
+{
+ return m_pendingFlush;
+}
+
+void QOhNativeVSync::setDeleted(bool deleted)
+{
+ m_backingStore = nullptr;
+ m_deleted = deleted;
+}
+
+void QOhNativeVSync::nativeVSyncFrameCallback(long long timestamp, void *data)
+{
+ // this callback will be invoked in other thread
+ Q_UNUSED(timestamp);
+ QOhNativeVSync *vsync = reinterpret_cast<QOhNativeVSync *>(data);
+ if (vsync->m_deleted) {
+ QtOh::runOnQtMainThread([vsync] {
+ delete vsync;
+ });
+ return;
+ }
+ vsync->m_ready.store(true);
+ if (vsync->m_pendingFlush) {
+ vsync->requestFlush();
+ }
+ OH_NativeVSync_RequestFrame(vsync->m_vsync->object(), &QOhNativeVSync::nativeVSyncFrameCallback, vsync);
+}
+
+void QOhNativeVSync::requestFlush()
+{
+ if (m_window.isNull())
+ return;
+
+ // this should invoked in qt thread, ensure QOhNativeVSync is alived in Qt thread.
+ QMetaObject::invokeMethod(m_window.data(), [this]() {
+ if (m_deleted)
+ return;
+ if (isReady() && isPendingFlush()) {
+ flush();
+ }
+ }, Qt::QueuedConnection);
+}
+
+void QOhNativeVSync::flush()
+{
+ if (m_window.isNull() || m_backingStore == nullptr)
+ return;
+
+ m_pendingFlush = false;
+ auto region = m_region;
+ auto offset = m_offset.value_or(QPoint());
+ m_region = {};
+ m_offset = std::nullopt;
+ m_backingStore->flush(m_window.data(), region, offset);
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,44 @@
+#ifndef QOHNATIVEVSYNC_H
+#define QOHNATIVEVSYNC_H
+
+#include "qohobjectholder.h"
+#include <QScopedPointer>
+#include <QRegion>
+#include <QPoint>
+#include <QPointer>
+#include <QWindow>
+
+#include <native_vsync/native_vsync.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformBackingStore;
+
+class QOhNativeVSync
+{
+public:
+ QOhNativeVSync(QWindow *window, QPlatformBackingStore *backingStore);
+ void saveFrame(QWindow *wnd, const QRegion ®ion, const QPoint &offset);
+ bool isValid() const;
+ bool isReady() const;
+ void clearFlag();
+ bool isPendingFlush() const;
+ void setDeleted(bool deleted);
+ void flush();
+
+private:
+ static void nativeVSyncFrameCallback(long long timestamp, void *data);
+ void requestFlush();
+ QScopedPointer<QOhObjectHolder<OH_NativeVSync>> m_vsync;
+ QPointer<QWindow> m_window;
+ QPlatformBackingStore *m_backingStore;
+ std::atomic_bool m_ready = ATOMIC_VAR_INIT(true);
+ bool m_pendingFlush = false;
+ bool m_deleted = false;
+ QRegion m_region;
+ std::optional<QPoint> m_offset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHNATIVEVSYNC_H
new file mode 100644
@@ -0,0 +1,1118 @@
+#include <QDebug>
+#include <QMargins>
+#include <QBitArray>
+#include <QTimer>
+#include <QJsModule>
+#include <QScopeGuard>
+#include <QSharedPointer>
+#include <QLoggingCategory>
+#include <qpa/qplatformscreen.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <private/qguiapplication_p.h>
+#include <private/qwindow_p.h>
+#include <window_manager/oh_window_comm.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <window_manager/oh_window_event_filter.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+
+#include "qohplatforminputcontext.h"
+#include "qohevent.h"
+#include "qoheventdispatcher.h"
+#include "qohmain.h"
+#include "qohutility.h"
+#include "qjsability.h"
+#include "qjscontext.h"
+#include "qohauxiliary.h"
+#include "qopenharmony.h"
+#include "qohnormalwindow.h"
+#include "qjswindowstage.h"
+#include "qopenharmonydefines.h"
+#include "qohnativewindow.h"
+#include "qohnativewindowmanager.h"
+#include "qohjsonlistener.h"
+#include "qohplatformopenglwindow.h"
+#include "qohplatformscreen.h"
+#include "qohwindowcontext.h"
+#include "qjssettings.h"
+#if OHOS_SDK_VERSION > 13
+#include "qohdisplay.h"
+#include "qohdisplaymanager.h"
+#endif
+#include <multimedia/image_framework/image/pixelmap_native.h>
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhNativeWindow::AvoidArea QOhNativeWindow::jsAvoidArea2Qt(Napi::Object result)
+{
+ QOhNativeWindow::AvoidArea area;
+ area.visible = result.Get("visible").ToBoolean();
+ Napi::Object jsRect = result.Get("leftRect").As<Napi::Object>();
+ area.leftRect = QtOh::jsRect2QRect(jsRect);
+ jsRect = result.Get("topRect").As<Napi::Object>();
+ area.topRect = QtOh::jsRect2QRect(jsRect);
+ jsRect = result.Get("rightRect").As<Napi::Object>();
+ area.rightRect = QtOh::jsRect2QRect(jsRect);
+ jsRect = result.Get("bottomRect").As<Napi::Object>();
+ area.bottomRect = QtOh::jsRect2QRect(jsRect);
+ return area;
+}
+
+QOhNativeWindow::QOhNativeWindow(QOhNativeWindow *parent)
+ : QJsObject(Napi::Object())
+ , m_ability(nullptr)
+ , m_parentNativeWindow(parent)
+ , m_window(nullptr)
+ , m_touchOutsideListener(nullptr)
+ , m_windowModule(nullptr)
+{
+ m_isWindowDestroyed.storeRelease(false);
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(QtOh::WindowStatusType::FLOATING));
+
+ s_allWindows << this;
+}
+
+QOhNativeWindow::~QOhNativeWindow()
+{
+ qDeleteAll(m_listeners);
+ m_listeners.clear();
+ if (m_touchOutsideListener != nullptr) {
+ delete m_touchOutsideListener;
+ m_touchOutsideListener = nullptr;
+ }
+
+ s_allWindows.removeAll(this);
+}
+
+void QOhNativeWindow::attachAbility(QJsAbility *ability)
+{
+ m_ability = ability;
+}
+
+QJsAbility *QOhNativeWindow::ability() const
+{
+ if (m_ability)
+ return m_ability;
+ // 子窗口没有ability,查找父窗口的
+ QOhNativeWindow *p = m_parentNativeWindow;
+ while (p) {
+ if (p->m_ability)
+ return p->m_ability;
+ p = p->m_parentNativeWindow;
+ }
+
+ return nullptr;
+}
+
+QOhNativeWindow *QOhNativeWindow::createSubWindow(QOhPlatformOpenGLWindow *window)
+{
+ QScopedPointer<QOhNativeWindow> nativeWindow(new QOhNormalWindow(this));
+ QOhNativeWindow *resultWindow = nativeWindow.data();
+ auto geo = window->geometry();
+ auto *platformScreen = QOhDisplayManager::instance()->screenAt(geo.center());
+ if (platformScreen) {
+ window->window()->setScreen(platformScreen->screen());
+ }
+ if (!this->ability())
+ return nullptr;
+ auto stage = this->ability()->context()->windowStage();
+ bool result = safeExecWithPromise<bool>([this, window, stage, resultWindow](auto p) {
+ Napi::Object option = Napi::Object::New(env());
+ option.Set("title", window->windowTitle().toStdString());
+ option.Set("isModal", window->isModal());
+ option.Set("decorEnabled", window->hasFrame());
+ if (QtOh::apiVersion() >= 14) {
+ Qt::WindowModality m = window->modality();
+ if (Qt::WindowModal == m) {
+ option.Set("modalityType", Napi::Number::New(env(), WINDOW_MODALITY));
+ } else if (Qt::ApplicationModal == m) {
+ auto type = ((QtOh::isTabletDevice() || QtOh::isPhoneDevice()) && !QtOh::isFreeWindowEnable()) ?
+ WINDOW_MODALITY : APPLICATION_MODALITY;
+ option.Set("modalityType", Napi::Number::New(env(), type));
+ }
+ }
+
+ Q_UNUSED(stage)
+ Napi::Value result = call("createSubWindowWithOptions",
+ { Napi::String::New(env(), window->windowName().toStdString()), option });
+ if (!result.IsPromise()) {
+ p->set_value(false);
+ return;
+ }
+
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([this, resultWindow, window, p](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1 || !resultWindow->attachNativeWindow(info[0].As<Napi::Object>())) {
+ p->set_value(false);
+ return ;
+ }
+
+ p->set_value(true);
+ }).onCatch([this, p](const Napi::CallbackInfo& info) {
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0) {
+ LOGW("create window failed, code is %{public}d, message is %{public}s",
+ code, error.Get("message").ToString().Utf8Value().c_str());
+ }
+ p->set_value(false);
+ });
+ });
+ return result ? nativeWindow.take() : nullptr;
+}
+
+QOhNativeWindow *QOhNativeWindow::createFloatSubWindow(QOhPlatformOpenGLWindow *window)
+{
+ QScopedPointer<QOhNativeWindow> nativeWindow(new QOhNormalWindow(this));
+ QOhNativeWindow *resultWindow = nativeWindow.data();
+
+ auto geo = window->geometry();
+ auto *platformScreen = QOhDisplayManager::instance()->screenAt(geo.center());
+ if (platformScreen) {
+ window->window()->setScreen(platformScreen->screen());
+ }
+
+ bool result = safeExecWithPromise<bool>([this, window, resultWindow](auto p) {
+ QJsAbility *ability = this->ability();
+ Q_ASSERT(ability != nullptr);
+ QJsContext *context = ability->context();
+
+ if (m_windowModule.isNull())
+ m_windowModule.reset(new QJsModule("@ohos.window"));
+
+ Napi::Object config = Napi::Object::New(env());
+ config.Set("name", Napi::String::New(env(), window->windowName().toStdString()));
+ config.Set("windowType", 8);
+ config.Set("ctx", context->object());
+
+ Napi::Value result = m_windowModule->call("createWindow",{config});
+ if (!result.IsPromise()) {
+ p->set_value(false);
+ return;
+ }
+
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([this, resultWindow, window, p](const Napi::CallbackInfo& info) {
+ if (info.Length() > 0 && resultWindow->attachNativeWindow(info[0].As<Napi::Object>())){
+ resultWindow->setWindowFocusable(!window->showWithoutActive());
+ p->set_value(true);
+ } else {
+ p->set_value(false);
+ }
+ }).onCatch([p](const Napi::CallbackInfo& info){ Q_UNUSED(info); p->set_value(false); });
+ });
+ return result ? nativeWindow.take() : nullptr;
+}
+
+void QOhNativeWindow::setParentNativeWindow(QOhNativeWindow *parent)
+{
+ m_parentNativeWindow = parent;
+}
+
+void QOhNativeWindow::setWindowTitleVisible(bool visible)
+{
+ m_titleVisible = visible;
+
+ if (!QtOh::isSupportFreeWindow()) {
+ LOGW("setWindowDecorVisible can only be used in the free multi window mode of "
+ "2in1 devices or tablet devices");
+ return;
+ }
+
+ safeExec([this, visible] { call("setWindowDecorVisible", visible); });
+}
+
+void QOhNativeWindow::setWindowFocusable(bool focusable)
+{
+ m_focusable = focusable;
+ safeExec([this] { call("setWindowFocusable", m_focusable); });
+}
+
+void QOhNativeWindow::setWindowTitleButtonVisible(bool hasMaxButton, bool hasMinButton, bool hasCloseButton)
+{
+ /* 只有主窗口才能设置 */
+ if (m_parentNativeWindow || m_isSplashWindow)
+ return;
+
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA, "setWindowTitleButtonVisible This interface is only "
+ "available in Free Multi-Window Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+
+ safeExec([&] {
+ if (QtOh::apiVersion() < 14) {
+ call("setWindowTitleButtonVisible", {Napi::Boolean::New(env(), hasMaxButton),
+ Napi::Boolean::New(env(), hasMinButton)});
+ } else {
+ call("setWindowTitleButtonVisible", {Napi::Boolean::New(env(), hasMaxButton),
+ Napi::Boolean::New(env(), hasMinButton),
+ Napi::Boolean::New(env(), hasCloseButton)});
+ }
+ });
+}
+
+/**
+ * @note need permission: ohos.permission.WINDOW_TOPMOST
+ */
+void QOhNativeWindow::setWindowTopmost(bool isWindowTopmost)
+{
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA, "setWindowTopmost This interface is only "
+ "available in Free Multi-Window Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+
+ // 只有主窗口才能设置
+ if (m_parentNativeWindow || m_isSplashWindow)
+ return;
+
+ safeExec([this, isWindowTopmost] { call("setWindowTopmost", {Napi::Boolean::New(env(), isWindowTopmost)}); });
+}
+
+static QString colorToHexRgbaString(const QColor &color) {
+ QString hexRgbString = color.name();
+ int alpha = color.alpha();
+ QString alphaHex = QString("%1").arg(alpha, 2, 16, QLatin1Char('0'));
+ QString hexRgbaString = hexRgbString + alphaHex;
+ return hexRgbaString;
+}
+
+void QOhNativeWindow::setWindowContainterColor(const QColor &activeColor, const QColor &inactiveColor)
+{
+ if (QtOh::Utility::type() != QtOh::Utility::PC) {
+ qCWarning(openharmonyQPA, "setWindowContainterColor This interface is only available in 2-in-1 devices.");
+ return;
+ }
+
+ asynSafeExec([=, this] {
+ call("setWindowContainterColor", colorToHexRgbaString(activeColor),
+ colorToHexRgbaString(inactiveColor));
+ });
+}
+
+void QOhNativeWindow::raise()
+{
+ HiTracer tracer("QOhNativeWindow::raise");
+ bool visible = isVisible();
+
+ if (!visible) {
+ return;
+ }
+ raiseNativeWindow();
+}
+
+void QOhNativeWindow::lower()
+{
+
+}
+
+void QOhNativeWindow::setVisible(bool visible)
+{
+ if (visible) {
+ safeExec([this] {
+ if (isVisible())
+ return;
+
+ Napi::Function showCallback = Napi::Function::New(env(), [](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1)
+ return;
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0)
+ LOGW("showWindow failed, code is %{public}d, message is %{public}s",
+ code, error.Get("message").ToString().Utf8Value().c_str());
+ });
+ call("showWindow", {showCallback});
+ });
+ } else {
+ showMinimized();
+ }
+}
+
+void QOhNativeWindow::showFullScreen()
+{
+ if (!isTopWindow() || !canShowWindow())
+ return;
+
+ safeExec([this] {
+ auto s = windowStatus();
+ if (s == QtOh::WindowStatusType::FULL_SCREEN)
+ return;
+ if (s == QtOh::WindowStatusType::MINIMIZE) {
+ call("restore");
+ }
+ if (QtOh::apiVersion() < 14) {
+ call("maximize", {Napi::Number::New(env(), ENTER_IMMERSIVE)});
+ } else {
+ if (QtOh::isFreeWindowEnable()) {
+ call("setWindowLayoutFullScreen", {Napi::Value::From(env(), true)} );
+ call("maximize", {Napi::Number::New(env(), FOLLOW_APP_IMMERSIVE_SETTING)});
+ call("setSpecificSystemBarEnabled", {Napi::String::New(env(), "status"),
+ Napi::Value::From(env(), false)});
+ call("setSpecificSystemBarEnabled", {Napi::String::New(env(), "navigationIndicator"),
+ Napi::Value::From(env(), false)});
+ } else {
+ call("maximize", {Napi::Number::New(env(), ENTER_IMMERSIVE_DISABLE_TITLE_AND_DOCK_HOVER)});
+ call("setSpecificSystemBarEnabled", {Napi::String::New(env(), "status"),
+ Napi::Value::From(env(), false)});
+ call("setSpecificSystemBarEnabled", {Napi::String::New(env(), "navigationIndicator"),
+ Napi::Value::From(env(), false)});
+ }
+ }
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(QtOh::WindowStatusType::FULL_SCREEN));
+ });
+}
+
+void QOhNativeWindow::showMaximized()
+{
+ HiTracer tracer("QOhNativeWindow::showMaximized");
+ if (!canShowWindow())
+ return;
+
+ safeExec([this] {
+ auto s = windowStatus();
+ if (s == QtOh::WindowStatusType::MAXIMIZE)
+ return;
+ if (s == QtOh::WindowStatusType::MINIMIZE) {
+ if (isTopWindow()) {
+ call("restore");
+ } else {
+ call("showWindow");
+ }
+ }
+ call("maximize", {Napi::Number::New(env(), EXIT_IMMERSIVE)});
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(QtOh::WindowStatusType::MAXIMIZE));
+ });
+}
+
+void QOhNativeWindow::showMinimized()
+{
+ HiTracer tracer("QOhNativeWindow::showMinimized");
+ if (!canShowWindow())
+ return;
+
+ safeExecWithPromise<void>([this](auto p) {
+ /* 子窗口的状态会跟随父窗口的状态,如果父窗口显示,子窗口没有最小化,会跟着一起显示 */
+ Napi::Value result = call("minimize");
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(QtOh::WindowStatusType::MINIMIZE));
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([&, p](const Napi::CallbackInfo&) {
+ p->set_value();
+ }).onCatch([p](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info)
+ p->set_value();
+ });
+ });
+}
+
+void QOhNativeWindow::requestFocusable()
+{
+
+}
+
+void QOhNativeWindow::setMask(const QRegion ®ion, const QRect &rect)
+{
+ if (region.isEmpty()) {
+ LOGW("set mask failed: region is empty");
+ return;
+ }
+
+ if (rect.isEmpty()) {
+ LOGW("set mask failed: rect is empty");
+ return;
+ }
+
+ auto func = [this, rect, region = region] {
+ const int width = rect.width();
+ const int height = rect.height();
+ std::vector<uint8_t> maskData(height * width, 0);
+
+ for (const QRect& area : region) {
+ const int yStart = std::clamp(area.top(), 0, height-1);
+ const int yEnd = std::clamp(area.bottom(), 0, height-1);
+ const int xStart = std::clamp(area.left(), 0, width-1);
+ const int xEnd = std::clamp(area.right(), 0, width-1);
+ for (int y = yStart; y <= yEnd; ++y) {
+ auto* rowStart = maskData.data() + y * width + xStart;
+ std::fill_n(rowStart, xEnd - xStart + 1, 1);
+ }
+ }
+
+ napi_value jsArray;
+ napi_create_array_with_length(env(), height, &jsArray);
+ napi_value jsZero = Napi::Number::New(env(), 0);
+ napi_value jsOne = Napi::Number::New(env(), 1);
+
+ for (int y = 0; y < height; ++y) {
+ napi_value rowArray;
+ napi_create_array_with_length(env(), width, &rowArray);
+
+ const int rowOffset = y * width;
+ for (int x = 0; x < width; ++x) {
+ const napi_value value = maskData[rowOffset + x] ? jsOne : jsZero;
+ napi_set_element(env(), rowArray, x, value);
+ }
+ napi_set_element(env(), jsArray, y, rowArray);
+ }
+ call("setWindowMask", {jsArray});
+ };
+
+ asynSafeExec(func);
+}
+
+void QOhNativeWindow::showNormal()
+{
+ HiTracer tracer("QOhNativeWindow::showNormal");
+ if (!canShowWindow())
+ return;
+
+ safeExec([this] {
+ QtOh::WindowStatusType type = windowStatus();
+ if (type == QtOh::WindowStatusType::FULL_SCREEN ||
+ type == QtOh::WindowStatusType::MAXIMIZE ||
+ type == QtOh::WindowStatusType::SPLIT_SCREEN) {
+ call("recover");
+ } else if (type == QtOh::WindowStatusType::MINIMIZE) {
+ if (!m_parentNativeWindow && !m_isSplashWindow) {
+ if (QtOh::apiVersion() < 14) {
+ qNativeWindowManager->show(this);
+ } else {
+ call("restore");
+ call("recover");
+ }
+ } else {
+ setVisible(true);
+ }
+ } else {
+ setVisible(true);
+ }
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(QtOh::WindowStatusType::FLOATING));
+ });
+}
+
+void QOhNativeWindow::setWindowModal(bool isModal)
+{
+ if (QtOh::apiVersion() < 14)
+ return;
+
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA, "setWindowModal This interface is only"
+ " available in Free Multi-Window Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+
+ asynSafeExec([=, this] {
+ QJsWindowStage *stage = windowStage();
+ if (stage) {
+ stage->call("setWindowModal", { Napi::Value::From(env(), isModal) });
+ }
+ });
+}
+
+QJsWindowStage *QOhNativeWindow::windowStage()
+{
+ if (m_ability && m_ability->context())
+ return m_ability->context()->windowStage();
+
+ return nullptr;
+}
+
+void QOhNativeWindow::stopListener()
+{
+ for (int i = 0; i < m_listeners.count(); ++i) {
+ if (isDestroyed())
+ return;
+ m_listeners.at(i)->off();
+ }
+}
+
+void QOhNativeWindow::startListener()
+{
+ m_listeners << new QOhJsOnListener(this, "avoidAreaChange", [this](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1)
+ return;
+
+ QRect gtr = geometry();
+ if (gtr.isValid())
+ reportWindowGeometry();
+
+ Napi::Object avoidAreaOptions = info[0].As<Napi::Object>();
+ int t = avoidAreaOptions.Get("type").ToNumber();
+ QOhNativeWindow::AvoidAreaType type = (QOhNativeWindow::AvoidAreaType)t;
+ Napi::Object area = avoidAreaOptions.Get("area").As<Napi::Object>();
+ auto qtAvoidArea = jsAvoidArea2Qt(area);
+ m_avoidAreas.insert(type, qtAvoidArea);
+ if (type == TYPE_KEYBOARD) {
+ QtOh::runOnQtMainThread([this, qtAvoidArea]{
+ if (m_window == nullptr || !QOhNativeWindowManager::instance()->exist(this))
+ return;
+ auto rect = QHighDpi::fromNativePixels(qtAvoidArea.bottomRect, m_window->window());
+ QOhPlatformInputContext::ohInputContext()->setKeyboardRect(rect);
+ });
+ }
+ });
+
+ for (int i = 0; i < m_listeners.count(); ++i) {
+ m_listeners.at(i)->on();
+ }
+}
+
+void QOhNativeWindow::setWindowConerRadius(qreal radius)
+{
+ if (QtOh::apiVersion() < 17) {
+ return;
+ } else if (QtOh::apiVersion() < 20) {
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA,
+ "setWindowCornerRadius This interface is only available in Free Multi-Window "
+ "Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+ } else {
+ if (!QtOh::checkDeviceType(QtOh::Utility::Phone | QtOh::Utility::PC
+ | QtOh::Utility::Tablet)) {
+ qCWarning(openharmonyQPA,
+ "setWindowCornerRadius This interface is not support on this device");
+ return;
+ }
+ }
+
+ asynSafeExec([this, radius] { call("setWindowCornerRadius", { Napi::Number::New(env(), radius) }); });
+}
+
+void QOhNativeWindow::setWindowShadowRadius(qreal radius)
+{
+ if (!QtOh::checkDeviceType(QtOh::Utility::PC | QtOh::Utility::Tablet)) {
+ qCWarning(openharmonyQPA, "setWindowShadowRadius This interface is not support on this device");
+ return;
+ }
+
+ if (QtOh::apiVersion() >= 17){
+ asynSafeExec([this, radius] { call("setWindowShadowRadius", { Napi::Number::New(env(), radius) }); });
+ }
+}
+
+void QOhNativeWindow::setWindowBackgroundColor(const QColor &color)
+{
+ QString colorStr = color.name(QColor::HexArgb);
+ asynSafeExec([this, colorStr] {
+ call("setWindowBackgroundColor", { Napi::String::New(env(), colorStr.toStdString()) });
+ });
+}
+
+void QOhNativeWindow::setWindowTouchable(bool isTouchable)
+{
+ asynSafeExec([this, isTouchable] { call("setWindowTouchable", { Napi::Boolean::New(env(), isTouchable) }); });
+}
+
+void QOhNativeWindow::destroy()
+{
+ if (isDestroyed())
+ return;
+
+ stopListener();
+ m_window = nullptr;
+ setDestroyed();
+
+ if (m_ability) {
+ m_ability->terminateSelf();
+ delete m_ability;
+ m_ability = nullptr;
+ } else {
+ QtOh::runOnJsUIThreadWithPromise<void>([this](auto p) {
+ Napi::Promise ret = call("destroyWindow").As<Napi::Promise>();
+ QJsPromise promise(ret);
+ promise.onThen([p](const Napi::CallbackInfo &) {
+ LOGI("succeeded in destroying the window.");
+ p->set_value();
+ }).onCatch([p](const Napi::CallbackInfo &info) {
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ LOGW("Failed to destroy the window. Cause code: %{public}d, message: %{public}s",
+ code, error.Get("message").ToString().Utf8Value().c_str());
+ p->set_value();
+ });
+ });
+ }
+}
+
+void QOhNativeWindow::setDestroyed()
+{
+ m_isWindowDestroyed.storeRelease(true);
+}
+
+void QOhNativeWindow::handleFreeWindowEnableChanged(bool isFreeWindowEnable)
+{
+ if (!isTopWindow())
+ return;
+
+ if (isFreeWindowEnable && m_window && m_window->window()) {
+ Qt::WindowFlags flags = m_window->window()->flags();
+ m_window->setWindowFlags(flags);
+ }
+
+ safeExec([this, isFreeWindowEnable] {
+ if (isFreeWindowEnable) {
+ call("setWindowDecorVisible", {Napi::Boolean::New(env(), m_titleVisible)});
+ } else {
+ if (isVisible())
+ showFullScreen();
+ }
+ auto extraHandler = get("pcModeChangedListener");
+ if (extraHandler.IsEmpty() || !extraHandler.IsFunction())
+ return;
+ call("pcModeChangedListener", { Napi::Boolean::New(env(), isFreeWindowEnable) });
+ });
+}
+
+void QOhNativeWindow::checkForScreenChanged(uint32_t displayId)
+{
+#if OHOS_SDK_VERSION > 13
+ if (m_window == nullptr)
+ return;
+ m_displayId = displayId;
+ QPlatformScreen *currentScreen = m_window->screen();
+ QOhDisplayManager *displayManager = QOhDisplayManager::instance();
+ auto *platformScreen = displayManager->platformScreenOrNull((QOhDisplayId)displayId);
+
+ if (platformScreen && currentScreen != platformScreen) {
+ QWindowSystemInterface::handleWindowScreenChanged(m_window->window(), platformScreen->screen());
+ }
+#else
+ // Todo
+#endif
+}
+
+bool QOhNativeWindow::isVisible() const
+{
+ return safeExec([this] { return call<bool>("isWindowShowing"); });
+}
+
+bool QOhNativeWindow::isDestroyed() const
+{
+ return m_isWindowDestroyed.loadAcquire();
+}
+
+void QOhNativeWindow::reportWindowGeometry(const QRect &rectIn)
+{
+ Q_UNUSED(rectIn)
+}
+
+void QOhNativeWindow::raiseNativeWindow()
+{
+ if (isTopWindow()) {
+ //FIXME:分屏状态下调用showWindow会退出分屏
+ QtOh::WindowStatusType type = windowStatus();
+ if (QtOh::WindowStatusType::SPLIT_SCREEN != type) {
+ asynSafeExec([this] { call("showWindow"); });
+ }
+ } else {
+ if (QtOh::apiVersion() >= 14) {
+ asynSafeExec([&]() {
+ Napi::Promise promise = this->call("raiseToAppTop").As<Napi::Promise>();
+ if (!promise.IsNull()) {
+ QJsPromise p(promise);
+ p.onThen([&](const Napi::CallbackInfo &info){
+ Q_UNUSED(info)
+ LOGW("Succeeded in raising window to app top.");
+ }).onCatch([&](const Napi::CallbackInfo &info){
+ Napi::Object error = info[0].As<Napi::Object>();
+ Napi::Number code = error.Get("code").ToNumber();
+ Napi::String message = error.Get("message").ToString();
+ LOGW("raiseToAppTop failed, result code: %{public}d message: %{public}s", code.Int32Value(),
+ message.Utf8Value().c_str());
+ });
+ }
+ });
+ } else {
+ asynSafeExec([this] { call("showWindow"); });
+ }
+ }
+}
+
+
+QRect QOhNativeWindow::adjustRect(QWindow *window, const QRect &rectIn, const QMargins &effectiveMargins)
+{
+ QRect rect = rectIn;
+ if (rect.isValid()) {
+ int frameX = rect.x();
+ int frameY = rect.y();
+ int frameWidth = effectiveMargins.left() + rect.width() + effectiveMargins.right();
+ int frameHeight = effectiveMargins.top() + rect.height() + effectiveMargins.bottom();
+ const bool isDefaultPosition = !frameX && !frameY && window->isTopLevel();
+ const bool includeFrame = qt_window_private(const_cast<QWindow *>(window))->positionPolicy
+ == QWindowPrivate::WindowFrameInclusive;
+ if (!includeFrame && !isDefaultPosition) {
+ frameX -= effectiveMargins.left();
+ frameY -= effectiveMargins.top();
+ }
+ rect = QRect(frameX, frameY, frameWidth, frameHeight);
+ }
+ return rect;
+}
+
+uint32_t QOhNativeWindow::displayId() const
+{
+ return m_displayId;
+}
+
+QList<QOhNativeWindow *> QOhNativeWindow::allWindows()
+{
+ return s_allWindows;
+}
+
+QOhNativeWindow::AvoidArea QOhNativeWindow::windowAvoidArea(QOhNativeWindow::AvoidAreaType type) const
+{
+ return safeExec([this, type] {
+ Napi::Object result = call("getWindowAvoidArea", {Napi::Number::New(env(), type) }).As<Napi::Object>();
+ return jsAvoidArea2Qt(result);
+ });
+}
+
+void QOhNativeWindow::attachRootComponent(void *component)
+{
+ m_attachedComponent = component;
+ if (qNativeWindowManager->exist(this) && m_window != nullptr)
+ m_window->attachRootComponent(component);
+}
+
+void QOhNativeWindow::handleWindowEvent(QtOh::WindowState event)
+{
+ HiTracer tracer("QOhNativeWindow::handleWindowEvent");
+ if (!qNativeWindowManager->exist(this) || m_window == nullptr)
+ return;
+ QWindow *window = m_window->window();
+#if OHOS_SDK_VERSION < 18
+ if (event == QtOh::WindowState::WINDOW_DESTROYED) {
+ QOhEventDispatcher::instance()->handleWindowHidden(window);
+ QWindowSystemInterface::handleCloseEvent(window);
+ } else if (event == QtOh::WindowState::WINDOW_HIDDEN) {
+ QOhEventDispatcher::instance()->handleWindowHidden(window);
+#else
+ if (event == QtOh::WindowState::WINDOW_DESTROYED) {
+ QWindowSystemInterface::handleCloseEvent(window);
+ //QWindowSystemInterface::flushWindowSystemEvents();
+ } else if (event == QtOh::WindowState::WINDOW_HIDDEN) {
+ //fireExpose();
+#endif
+ } else if (event == QtOh::WindowState::WINDOW_SHOWN) {
+ auto wp = qt_window_private(m_window->window());
+ /*可能引起widget隐藏导致m_window被析构置null*/
+ if (!m_isShowSys && wp && !m_window->window()->isVisible() && isVisible()) {
+ wp->setVisible(true);
+ }
+ clearShowSysFlag();
+
+ if (m_window)
+ m_window->fireExpose(QRect(QPoint(), m_window->geometry().size()));
+
+ } else if (event == QtOh::WindowState::WINDOW_ACTIVE) {
+ handleFocusEvent(true, m_window);
+ } else if (event == QtOh::WindowState::WINDOW_INACTIVE) {
+ handleFocusEvent(false, m_window);
+ }
+}
+
+void QOhNativeWindow::handleWindowStatusEvent(QtOh::WindowStatusType event)
+{
+ if (qNativeWindowManager->exist(this) && m_window != nullptr) {
+ if (m_lastReportedStatusEvent != event) {
+ m_window->handleWindowStatusEvent(event);
+ m_lastReportedStatusEvent = event;
+ }
+ }
+}
+
+void QOhNativeWindow::updateWindowAvoidArea()
+{
+ asynSafeExec([this] {
+ Napi::Object result = call("getWindowAvoidArea", {Napi::Number::New(env(), TYPE_SYSTEM) }).As<Napi::Object>();
+ if (result.IsEmpty() || result.IsNull())
+ return;
+
+ m_avoidAreas.insert(TYPE_SYSTEM, jsAvoidArea2Qt(result));
+ result = call("getWindowAvoidArea", {Napi::Number::New(env(), TYPE_CUTOUT) }).As<Napi::Object>();
+ m_avoidAreas.insert(TYPE_CUTOUT, jsAvoidArea2Qt(result));
+ result = call("getWindowAvoidArea", {Napi::Number::New(env(), TYPE_SYSTEM_GESTURE) }).As<Napi::Object>();
+ m_avoidAreas.insert(TYPE_SYSTEM_GESTURE, jsAvoidArea2Qt(result));
+ result = call("getWindowAvoidArea", {Napi::Number::New(env(), TYPE_KEYBOARD) }).As<Napi::Object>();
+ m_avoidAreas.insert(TYPE_KEYBOARD, jsAvoidArea2Qt(result));
+ result = call("getWindowAvoidArea", {Napi::Number::New(env(), TYPE_NAVIGATION_INDICATOR) }).As<Napi::Object>();
+ m_avoidAreas.insert(TYPE_NAVIGATION_INDICATOR, jsAvoidArea2Qt(result));
+ });
+}
+
+void QOhNativeWindow::handleFocusEvent(bool focusIn, QOhPlatformWindow *platformWindow)
+{
+ QWindow *nextActiveWindow = nullptr;
+ if (focusIn) {
+ QWindow *topWindow = platformWindow->window();
+ // QFileDialog选择文件和取消之后,主窗口的激活事件会被先发送导致下面的检查isWindowBlocked
+ // 返回true,从而焦点切换失败 modalWindow是QFileDialogClassWindow,系统又没有重新发送激活事件
+// QWindow *modalWindow = nullptr;
+// if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) {
+// modalWindow->requestActivate();
+// return;
+// }
+ nextActiveWindow = topWindow;
+ } else {
+ QOhNativeWindow *focusNativeWindow = qNativeWindowManager->focusWindow();
+ if (focusNativeWindow != nullptr && focusNativeWindow->m_window != nullptr)
+ nextActiveWindow = focusNativeWindow->m_window->window();
+ }
+ QOhWindowContext::setFocusWindow(nextActiveWindow);
+}
+
+OH_PixelmapNative *QOhNativeWindow::grab()
+{
+ return safeExecWithPromise<OH_PixelmapNative *>([this](auto p) {
+ QJsPromise promise(call("snapshot").As<Napi::Promise>());
+ promise.onThen([&, p](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ p->set_value(nullptr);
+ return ;
+ }
+ OH_PixelmapNative *result = nullptr;
+ OH_PixelmapNative_ConvertPixelmapNativeFromNapi(env(), info[0], &result);
+ p->set_value(result);
+ }).onCatch([p](const Napi::CallbackInfo& info){
+ Q_UNUSED(info)
+ p->set_value(nullptr);
+ });
+ });
+}
+
+bool QOhNativeWindow::setMouseGrabEnabled(bool grab)
+{
+ // 如果不判断,右键菜单连续点击时,会导致右键菜单显示后又隐藏
+ static QList<QWindow *> s_hasTouchOutsideEvent;
+
+ if (!grab && m_window) {
+ s_hasTouchOutsideEvent.removeOne(m_window->window());
+ }
+ safeExec([this, grab] {
+ if (grab) {
+ if (m_touchOutsideListener == nullptr) {
+ m_touchOutsideListener = new QOhJsOnListener(this, "touchOutside", [this](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info);
+ if (m_window == nullptr)
+ return ;
+ auto window = QPointer<QWindow>(m_window->window());
+ s_hasTouchOutsideEvent << window;
+ QtOh::runOnQtMainThread([window]{
+ QWindow *findWindow = QGuiApplication::topLevelAt(QCursor::pos());
+ if (findWindow && findWindow->type() == Qt::Popup)
+ return;
+ // 让Qt内部处理pupup窗口的关闭,如果500ms之后还有popup窗口,在发送事件
+ QTimer::singleShot(500, [window]{
+ // 暂时只给popup窗口发送
+ if (window.isNull() || !s_hasTouchOutsideEvent.contains(window) || (window->type() != Qt::Popup)
+ || !QGuiApplicationPrivate::instance()->popupActive())
+ return;
+
+ QSharedPointer<QtOh::MouseEvent> event(new QtOh::MouseEvent());
+ QPoint screenPoint(0xFFFFFFF, 0xFFFFFFF);
+ event->scPos = screenPoint;
+ QPoint localPos = screenPoint;
+ event->xcPos = localPos;
+ event->window = window;
+ event->mouseButton = Qt::RightButton;
+ event->mouseEventType = QEvent::MouseButtonPress;
+ QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance();
+ dispatcher->appendOhEvent(event);
+ });
+ });
+ });
+ }
+ m_touchOutsideListener->on();
+ } else {
+ if (m_touchOutsideListener != nullptr) {
+ m_touchOutsideListener->off();
+ }
+ }
+ });
+ return true;
+}
+
+bool QOhNativeWindow::startSystemMove()
+{
+ if (QtOh::apiVersion() < 14) {
+ qCWarning(openharmonyQPA) << "only api great than 13 supported.";
+ return false;
+ }
+
+ if (!QtOh::checkDeviceType(QtOh::Utility::Phone | QtOh::Utility::PC | QtOh::Utility::Tablet)) {
+ qCWarning(openharmonyQPA, "startMoving This interface is not support on this device");
+ return false;
+ }
+
+ asynSafeExec([&] {
+ Napi::Value ret = call("startMoving");
+ if (ret.IsPromise()) {
+ QJsPromise promise(ret.As<Napi::Promise>());
+ promise.onThen([](const Napi::CallbackInfo &){
+ LOGI("Succeeded in starting moving window.");
+ }).onCatch([](const Napi::CallbackInfo &info){
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ LOGW("Failed to start moving window. Cause code: %{public}d, message: %{public}s",
+ code, error.Get("message").ToString().Utf8Value().c_str());
+ });
+ }
+ });
+ return true;
+}
+
+void QOhNativeWindow::setWindowTitleMoveEnabled(bool isEnabled)
+{
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA, "setWindowTitleMoveEnabled This interface is only"
+ " available in Free Multi-Window Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+
+ safeExec([=, this] { call("setWindowTitleMoveEnabled", { Napi::Boolean::New(env(), isEnabled)}); });
+}
+
+void QOhNativeWindow::setWindowTitle(const QString &title)
+{
+ if (!QtOh::isSupportFreeWindow()) {
+ qCWarning(openharmonyQPA, "setWindowTitle This interface is only"
+ " available in Free Multi-Window Mode on 2-in-1 devices or tablet devices.");
+ return;
+ }
+
+ safeExec([=, this] {
+ if (QtOh::apiVersion() >= 15) {
+ call("setWindowTitle", { Napi::String::New(env(), title.toStdString())});
+ } else {
+ if (isTopWindow() && m_ability != nullptr && m_ability->context()) {
+ m_ability->context()->call("setMissionLabel", title);
+ }
+ }
+ });
+}
+
+bool QOhNativeWindow::loadContent(const QString &name)
+{
+ m_name = name;
+ return true;
+}
+
+void QOhNativeWindow::attachPlatformWindow(QOhPlatformWindow *window)
+{
+ m_window = window;
+ if (m_window != nullptr) {
+ if (m_attachedComponent != nullptr)
+ m_window->attachRootComponent(m_attachedComponent);
+ qDeleteAll(m_listeners);
+ m_listeners.clear();
+ startListener();
+ updateWindowAvoidArea();
+ }
+}
+
+QString QOhNativeWindow::name() const
+{
+ return m_name;
+}
+
+bool QOhNativeWindow::isTopWindow() const
+{
+ return m_parentNativeWindow == nullptr;
+}
+
+void QOhNativeWindow::setSplashWindow()
+{
+ m_isSplashWindow = true;
+}
+
+bool QOhNativeWindow::attachNativeWindow(Napi::Object window)
+{
+ if (window.IsEmpty())
+ return false;
+ setObject(window);
+ return true;
+}
+
+/**
+ * @brief class QOhNativeWindow::WindowLimits::WindowLimits Start
+ */
+const QStringList windowLimitsKeys = {"minWidth", "minHeight", "maxWidth", "maxHeight"};
+
+QOhNativeWindow::WindowLimits::WindowLimits(QSize minimumSize, QSize maximumSize)
+{
+ qDebug() << "WindowLimits::WindowLimits"<< minimumSize << maximumSize;
+ if (windowLimitsKeys.size() != 4)
+ return;
+ /*
+ * 定义全局最小和最大尺寸
+ */
+ const QSize globalMinSize(1, 1);
+ const QSize globalMaxSize(3600, 3600);
+
+ /*
+ * 约束最小尺寸在全局范围内,约束最大尺寸在全局范围内
+ */
+ minimumSize = minimumSize.expandedTo(globalMinSize).boundedTo(globalMaxSize);
+ maximumSize = maximumSize.expandedTo(minimumSize).boundedTo(globalMaxSize);
+ m_values.clear();
+ m_values.append(minimumSize.width());
+ m_values.append(minimumSize.height());
+ m_values.append(maximumSize.width());
+ m_values.append(maximumSize.height());
+ qDebug() << *this;
+}
+
+QOhNativeWindow::WindowLimits::WindowLimits(Napi::Object value)
+{
+ if (windowLimitsKeys.size() != 4)
+ return;
+ for(int i = 0; i < 4; ++i) {
+ m_values[i] = int(value.Get(windowLimitsKeys[i].toStdString()).ToNumber());
+ }
+}
+
+QOhNativeWindow::WindowLimits &QOhNativeWindow::WindowLimits::intersect(const WindowLimits &other)
+{
+ if (m_values.size() == 4)
+ for (int i = 0; i < 4; ++i) {
+ m_values[i] = (i < 2) ? qMax(m_values[i], other.m_values[i]) : qMin(m_values[i], other.m_values[i]);
+ }
+ return *this;
+}
+
+QOhNativeWindow::WindowLimits &QOhNativeWindow::WindowLimits::operator +=(const QSize &size)
+{
+ if (windowLimitsKeys.size() == 4)
+ for (int i = 0; i < 4; ++i) {
+ m_values[i] += (i % 2 ? size.height() : size.width());
+ }
+ return *this;
+}
+
+Napi::Object QOhNativeWindow::WindowLimits::toObject(napi_env env)
+{
+ Napi::Object result = Napi::Object::New(env);
+ for (int i = 0; i < 4; ++i) {
+ result.Set(windowLimitsKeys[i].toStdString(), Napi::Number::New(env, m_values[i]));
+ }
+ return result;
+}
+
+QDebug operator<<(QDebug debug, const QOhNativeWindow::WindowLimits &limits)
+{
+ debug << "WindowLimits(";
+ for (int i = 0; i < 4; ++i) {
+ debug << windowLimitsKeys[i] << ":" << limits.m_values[i] << ",";
+ }
+ debug << ")";
+ return debug;
+}
+/**
+ * @brief class QOhNativeWindow::WindowLimits::WindowLimits End
+ */
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,265 @@
+#ifndef QOHNATIVEWINDOW_H
+#define QOHNATIVEWINDOW_H
+
+#include "qohplatformwindow.h"
+#include <qjsobject.h>
+#include <QPointer>
+#include <QSet>
+#include "qohauxiliary.h"
+#include <private/qopenharmony_p.h>
+struct OH_PixelmapNative;
+
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION >= 15)
+#include <window_manager/oh_window_event_filter.h>
+#endif
+QT_BEGIN_NAMESPACE
+class QJsAbility;
+class QJsWindowStage;
+class QOhJsOnListener;
+class QOhPlatformOpenGLWindow;
+
+class QOhNativeWindow : public QJsObject
+{
+protected:
+ enum MaximizePresentation{
+ FOLLOW_APP_IMMERSIVE_SETTING,
+ EXIT_IMMERSIVE,
+ ENTER_IMMERSIVE,
+ ENTER_IMMERSIVE_DISABLE_TITLE_AND_DOCK_HOVER /*api14*/
+ };
+
+ enum WindowModality {
+ WINDOW_MODALITY = 0,
+ APPLICATION_MODALITY
+ };
+
+ enum class WindowChangeReason {
+ UNDEFINED = 0, /* 默认值 */
+ MAXIMIZE, /* 窗口最大化 */
+ RECOVER, /* 窗口恢复到上一次的状态 */
+ MOVE, /* 窗口拖拽移动 */
+ DRAG, /* 窗口拖拽缩放 */
+ DRAG_START, /* 窗口开始拖拽缩放 */
+ DRAG_END /* 窗口结束拖拽缩放 */
+ };
+
+public:
+ enum AvoidAreaType {
+ TYPE_SYSTEM,
+ TYPE_CUTOUT,
+ TYPE_SYSTEM_GESTURE,
+ TYPE_KEYBOARD,
+ TYPE_NAVIGATION_INDICATOR
+ };
+ struct AvoidArea {
+ bool visible;
+ QRect leftRect;
+ QRect topRect;
+ QRect rightRect;
+ QRect bottomRect;
+ };
+
+ enum WindowType {
+ NORMAL_WINDOW,
+ EXTENSION_WINDOW
+ };
+
+ enum HitTest {
+ Client,
+ SystemButton
+ };
+
+ /**
+ * @brief The WindowLimits class
+ * 窗口尺寸限制
+ */
+ class WindowLimits
+ {
+ public:
+ WindowLimits(QSize minimumSize = QSize(1, 1), QSize maximumSize = QSize(3600, 3600));
+ WindowLimits(Napi::Object value);
+ /**
+ * @brief intersect
+ * 两个窗口限制的交集
+ * @param other
+ * @return 返回交集
+ */
+ WindowLimits &intersect(const WindowLimits &other);
+ /**
+ * @brief operator +=
+ * 窗口限制加上一个尺寸
+ * @param size 尺寸
+ * @return 返回加上尺寸后的窗口限制
+ */
+ WindowLimits& operator += (const QSize &size);
+ Napi::Object toObject(napi_env);
+ friend QDebug operator<<(QDebug, const WindowLimits &);
+ private:
+ QVector<int> m_values = {1, 1, 3600, 3600};
+ };
+
+ explicit QOhNativeWindow(QOhNativeWindow *parent = nullptr);
+ virtual ~QOhNativeWindow();
+
+ virtual WindowType nativeWindowType() const = 0;
+ QString name() const;
+
+ bool isTopWindow() const;
+ void setSplashWindow();
+
+ bool attachNativeWindow(Napi::Object window);
+
+ virtual void attachAbility(QJsAbility *ability);
+
+ virtual QJsAbility *ability() const;
+
+ virtual QOhNativeWindow *createSubWindow(QOhPlatformOpenGLWindow *window);
+
+ virtual QOhNativeWindow *createFloatSubWindow(QOhPlatformOpenGLWindow *window);
+
+ void setParentNativeWindow(QOhNativeWindow *parent);
+
+ virtual int32_t id() const = 0;
+ void attachPlatformWindow(QOhPlatformWindow *window);
+ virtual void stopListener();
+ virtual void startListener();
+
+ void setWindowConerRadius(qreal radius);
+ void setWindowShadowRadius(qreal radius);
+ void setWindowBackgroundColor(const QColor &color);
+ void setWindowTouchable(bool isTouchable);
+ void setWindowTitleVisible(bool visible);
+ void setWindowFocusable(bool focusable);
+ void setWindowTitleButtonVisible(bool hasMaxButton, bool hasMinButton, bool hasCloseButton);
+ void setWindowTopmost(bool isWindowTopmost);
+ void setWindowContainterColor(const QColor &activeColor, const QColor &inactiveColor);
+ virtual void requestFocus() = 0;
+ void raise();
+ void lower();
+ void setVisible(bool visible);
+ void showFullScreen();
+ void showMaximized();
+ void showMinimized();
+ void showNormal();
+ void setWindowModal(bool isModal = true);
+ QJsWindowStage *windowStage();
+ void requestFocusable();
+ virtual bool isFocused() = 0;
+ void setMask(const QRegion ®ion, const QRect &rect);
+ virtual bool setGeometry(const QRect &rectIn) = 0;
+ bool setMouseGrabEnabled(bool grab);
+ bool startSystemMove();
+ virtual QMargins frameMargins() const = 0;
+
+ OH_PixelmapNative *grab();
+
+ virtual QRect geometry() const = 0;
+ virtual QRect normalGeometry() const { return geometry(); }
+ void setWindowTitleMoveEnabled(bool isEnabled);
+ void setWindowTitle(const QString &title);
+ inline void setShowSysFlag() { m_isShowSys = true; }
+ inline void clearShowSysFlag() { m_isShowSys = false; }
+
+ virtual void setWindowLimits(WindowLimits &, bool force = true) = 0;
+ virtual WindowLimits getWindowLimits() = 0;
+ virtual bool loadContent(const QString &name);
+ void destroy();
+ void setDestroyed();
+ bool isDestroyed() const;
+ AvoidArea windowAvoidArea(AvoidAreaType type) const;
+ void handleFreeWindowEnableChanged(bool isFreeWindowEnable);
+
+ void attachRootComponent(void *component);
+ virtual QtOh::WindowStatusType windowStatus() const { return QtOh::WindowStatusType::FLOATING; }
+ bool isVisible() const;
+ virtual bool canShowWindow() const { return true; }
+ virtual QRect titleButtonRect() const { return QRect(); }
+ virtual HitTest hitTest(const QPoint &point, qreal scale) const { Q_UNUSED(point) Q_UNUSED(scale) return Client; }
+ virtual void reportWindowGeometry(const QRect &rectIn = QRect());
+ void raiseNativeWindow();
+ static QRect adjustRect(QWindow *window, const QRect &rectIn, const QMargins &effectiveMargins);
+ static QList<QOhNativeWindow *> allWindows();
+ uint32_t displayId() const;
+
+protected:
+ void handleWindowEvent(QtOh::WindowState event);
+ void handleWindowStatusEvent(QtOh::WindowStatusType event);
+ void checkForScreenChanged(uint32_t displayId);
+
+private:
+ void updateWindowAvoidArea();
+ void handleFocusEvent(bool focusIn, QOhPlatformWindow *platformWindow);
+
+protected:
+ static AvoidArea jsAvoidArea2Qt(Napi::Object result);
+ static inline QList<QOhNativeWindow *> s_allWindows = {};
+ QJsAbility *m_ability = nullptr;
+ QOhNativeWindow *m_parentNativeWindow;
+ QOhPlatformWindow *m_window = nullptr;
+ QHash<AvoidAreaType, AvoidArea> m_avoidAreas;
+ QList<QOhJsOnListener *> m_listeners;
+ QOhJsOnListener *m_touchOutsideListener = nullptr;
+ QString m_name;
+ QScopedPointer<QJsModule> m_windowModule;
+ uint32_t m_displayId = 0;
+ QtOh::WindowStatusType m_lastReportedStatusEvent = QtOh::WindowStatusType::UNDEFINED;
+ QAtomicInteger<int32_t> m_expectedWindowStatus;
+ void *m_attachedComponent = nullptr;
+ bool m_isSplashWindow = false;
+ bool m_focusable = false;
+ bool m_titleVisible = true;
+ bool m_isShowSys = false;
+
+private:
+ QAtomicInteger<bool> m_isWindowDestroyed;
+
+protected:
+ template <typename Function, typename... Args>
+ auto safeExec(Function &&func, Args&&... args) const
+ -> decltype(std::invoke(std::forward<Function>(func), std::forward<Args>(args)...)) {
+
+ using ReturnType = decltype(std::invoke(std::forward<Function>(func), std::forward<Args>(args)...));
+ if constexpr (std::is_same_v<ReturnType, void>) {
+ QtOh::runOnJsUIThreadAndWait([this, func = std::forward<Function>(func)] {
+ if (isDestroyed())
+ return;
+
+ func();
+ });
+ } else {
+ return QtOh::runOnJsUIThreadWithResult(
+ [this, func = std::forward<Function>(func), ...args = std::forward<Args>(args)]() mutable {
+ if (isDestroyed())
+ return ReturnType{};
+
+ return std::invoke(std::forward<Function>(func), std::forward<Args>(args)...);
+ });
+ }
+ }
+
+ template <typename ReturnType, typename Function, typename... Args>
+ auto safeExecWithPromise(Function &&func, Args&&... args) const ->ReturnType {
+ return QtOh::runOnJsUIThreadWithPromise<ReturnType>(
+ [this, func = std::forward<Function>(func), ...args = std::forward<Args>(args)](auto&& p) mutable {
+ if (isDestroyed()) {
+ if constexpr (std::is_void_v<ReturnType>) {
+ p->set_value();
+ } else {
+ p->set_value(ReturnType{});
+ }
+ return;
+ }
+
+ std::invoke(std::forward<Function>(func), p, std::forward<Args>(args)...);
+ });
+ }
+
+ void asynSafeExec(const std::function<void()> &func) const {
+ QtOh::runOnJsUIThreadNoWait([=, this]{
+ if (!isDestroyed())
+ func();
+ });
+ }
+};
+QT_END_NAMESPACE
+#endif // QOHNATIVEWINDOW_H
new file mode 100644
@@ -0,0 +1,411 @@
+#include "qdebug.h"
+#include "qohmain.h"
+#include "qohnativewindow.h"
+#include "qohnormalwindow.h"
+#include "qohnativewindowmanager.h"
+#include "qohextensionwindow.h"
+#include "qohplatformopenglwindow.h"
+#include "qjsaccessmanager.h"
+#include <qpa/qwindowsysteminterface_p.h>
+#include "qohwindownode.h"
+#include "qjsuiability.h"
+#include "qohauxiliary.h"
+#include "qjsuiabilitycontext.h"
+#include "qjswindowstage.h"
+#include "qjswant.h"
+#include "qjsabilityfactory.h"
+#include "qohwindowcontext.h"
+#include "qohplatformscreen.h"
+
+#include <QTimer>
+#include <QWindow>
+#include <QThread>
+#include <QScopeGuard>
+#include <QReadLocker>
+#include <QWriteLocker>
+#include <QElapsedTimer>
+#include <QLoggingCategory>
+#include <QGuiApplication>
+#include <qpa/qplatformnativeinterface.h>
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include <qopenharmonydefines.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+#include "qohdisplaymanager.h"
+#include "qjsuiextensionability.h"
+#include "qjsuiextensioncontextsession.h"
+
+Q_GLOBAL_STATIC(QOhNativeWindowManager, manager)
+
+QOhNativeWindowManager::QOhNativeWindowManager()
+ : m_firstTopWindow(nullptr)
+{
+
+}
+
+QOhNativeWindowManager::~QOhNativeWindowManager()
+{
+
+}
+
+QOhNativeWindow *QOhNativeWindowManager::createNativeWindow(QOhPlatformOpenGLWindow *platformWindow, const QRect &rect)
+{
+ QOhPlatformOpenGLWindow::Kind kind = platformWindow->windowKind();
+ qCWarning(openharmonyQPA) << "create js window, kind: " << int(kind);
+ Q_ASSERT(kind != QOhPlatformOpenGLWindow::Kind::NativeNode);
+
+ QOhNativeWindow *window = nullptr;
+ /* FIXME SplashScreen暂时做特殊处理 */
+ if ((kind == QOhPlatformOpenGLWindow::Kind::SubWindow) ||
+ (kind == QOhPlatformOpenGLWindow::Kind::SplashWindow)) {
+ window = createSubWindow(platformWindow);
+ } else if (kind == QOhPlatformOpenGLWindow::Kind::MainWindow) {
+ window = createTopWindow(platformWindow, rect);
+ }
+ if (window != nullptr && (kind == QOhPlatformOpenGLWindow::Kind::SplashWindow)) {
+ window->setSplashWindow();
+ }
+ return window;
+}
+
+QOhNativeWindow *QOhNativeWindowManager::focusWindow()
+{
+ // 是否提供系统接口直接获取?
+ return find([](QOhNativeWindow *w){
+ return w->isFocused();
+ });
+}
+
+bool QOhNativeWindowManager::exist(QOhNativeWindow *window)
+{
+ if (window == nullptr)
+ return false;
+ QReadLocker readLocker(&m_windowsLock);
+ return m_windows.contains(window);
+}
+
+void QOhNativeWindowManager::destroyWindow(QOhNativeWindow *window)
+{
+ if (window == nullptr || !m_windows.contains(window))
+ return;
+ {
+ QWriteLocker writeLocker(&m_windowsLock);
+ m_windows.removeOne(window);
+ }
+ window->destroy();
+ delete window;
+ window = nullptr;
+}
+
+QOhNativeWindowManager *QOhNativeWindowManager::instance()
+{
+ return manager();
+}
+
+QJsAbility *QOhNativeWindowManager::defaultAbility() const
+{
+ if (m_windows.isEmpty())
+ return nullptr;
+ return m_windows.first()->ability();
+}
+
+void QOhNativeWindowManager::destoryAllAbility()
+{
+ for (auto &w : m_windows) {
+ if (w->isTopWindow())
+ w->ability()->terminateSelf();
+ }
+}
+
+NativeResourceManager *QOhNativeWindowManager::resourceManager()
+{
+ if (m_windows.isEmpty())
+ return nullptr;
+ QJsAbility *ability = m_windows.first()->ability();
+ QJsContext *context = ability->context();
+ return context->resourceManager();
+}
+
+Napi::Value QOhNativeWindowManager::jsResourceManager()
+{
+ if (m_windows.isEmpty())
+ return Napi::Value();
+ QJsAbility *ability = m_windows.first()->ability();
+ QJsContext *context = ability->context();
+ return context->get("resourceManager");
+}
+
+QJsContext *QOhNativeWindowManager::context(QWindow *window) const
+{
+ if (m_windows.isEmpty())
+ return nullptr;
+ QJsContext *result = m_windows.last()->ability()->context();
+ if (window == nullptr) {
+ return result;
+ }
+ QOhWindowNode *node = QOhWindowNode::fromId(window->winId());
+ if (node == nullptr)
+ return result;
+ QOhNativeWindow *nativeWindow = node->ownerWindow();
+ if (nativeWindow == nullptr)
+ return result;
+ result = nativeWindow->ability()->context();
+ return result;
+}
+
+QOhNativeWindow *QOhNativeWindowManager::window(const QString &name) const
+{
+ return find([name](QOhNativeWindow *w){
+ return w->name() == name;
+ });
+}
+
+QOhNativeWindow *QOhNativeWindowManager::find(std::function<bool (QOhNativeWindow *)> function) const
+{
+ QReadLocker readLocker(&m_windowsLock);
+ auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), function);
+ return it == m_windows.constEnd() ? nullptr : *it;
+}
+
+QOhNativeWindow *QOhNativeWindowManager::createSubWindow(QOhPlatformOpenGLWindow *platformWindow)
+{
+ auto qWindow = platformWindow->window();
+ auto parent = dynamic_cast<QOhPlatformOpenGLWindow *>(platformWindow->parent());
+ QOhNativeWindow *parentNativeWindow = nullptr;
+ if (parent != nullptr) {
+ parentNativeWindow = parent->nativeWindow();
+ }
+ if (parentNativeWindow == nullptr) {
+ auto transientParent = qWindow->transientParent();
+ if (transientParent != nullptr) {
+ if (QOhPlatformOpenGLWindow *p = dynamic_cast<QOhPlatformOpenGLWindow*>(transientParent->handle())) {
+ parentNativeWindow = p->nativeWindow();
+ }
+ }
+ }
+ if (parentNativeWindow == nullptr && !m_windows.empty()) {
+ parentNativeWindow = m_windows.first();
+ }
+ if (parentNativeWindow == nullptr)
+ return nullptr;
+
+ Qt::WindowFlags flags = qWindow->flags();
+ QOhNativeWindow *nativeWindow = nullptr;
+
+ if (flags.testFlag(Qt::Floating)) {
+ static bool hasPermission = (QJsAccessManager::instance()->checkPermission("ohos.permission.SYSTEM_FLOAT_WINDOW") == 0);
+
+ qCInfo(openharmonyQPA) << "QOhJsWindowManager::createFloatSubWindow"
+ << "parent:" << qWindow->parent()
+ << "transientParent:" << qWindow->transientParent();
+ if (!hasPermission) {
+ qCWarning(openharmonyQPA) << "No floating window permission.";
+ return nullptr;
+ }
+ nativeWindow = parentNativeWindow->createFloatSubWindow(platformWindow);
+ } else {
+ qCInfo(openharmonyQPA) << "QOhJsWindowManager::createSubWindow "
+ << "parent:" << qWindow->parent()
+ << "transientParent:" << qWindow->transientParent();
+ nativeWindow = parentNativeWindow->createSubWindow(platformWindow);
+ }
+ if (nativeWindow != nullptr) {
+ m_windows << nativeWindow;
+
+ platformWindow->nativeWindowNode()->setOwnerWindow(nativeWindow);
+ nativeWindow->attachPlatformWindow(platformWindow);
+ nativeWindow->loadContent(platformWindow->windowName());
+ }
+ return nativeWindow;
+}
+
+QOhNativeWindow *QOhNativeWindowManager::createTopWindow(QOhPlatformOpenGLWindow *platformWindow, const QRect &rect)
+{
+ QOhNativeWindow *window = nullptr;
+ if (m_firstTopWindow != nullptr) {
+ window = m_firstTopWindow;
+ m_firstTopWindow = nullptr;
+ platformWindow->setWindowName(window->name());
+ // 第一个窗口系统创建,监听不到ACTIVE事件,自动触发
+ QOhWindowContext::setFocusWindow(platformWindow->window());
+ } else {
+ QString name = platformWindow->windowName();
+ m_topWindowPromise = std::make_shared<std::promise<QOhNativeWindow *>>();
+ auto feature = m_topWindowPromise->get_future();
+ if(m_windows.isEmpty()) {
+ return nullptr;
+ }
+ QJsAbility *ability = m_windows.first()->ability();
+ if (ability == nullptr) {
+ qWarning() << "Not find valid ability to create top window";
+ return nullptr;
+ }
+ QtOh::runOnJsUIThreadAndWait([this, ability, platformWindow, rect, name]{
+ QJsContext *context = ability->context();
+ QJsWant *want = ability->launchWant();
+ Want startWant = { want->bundleName(), want->moduleName() ,want->abilityName(), {
+ name,
+ name}
+ };
+ QOhPlatformScreen *platformScreen = static_cast<QOhPlatformScreen*>(platformWindow->screen());
+ StartOptions options = {
+ .windowMode = -1,
+ .windowLeft = rect.x(),
+ .windowTop = rect.y(),
+ .windowWidth = rect.width(),
+ .windowHeight = rect.height(),
+ .displayId = platformScreen->displayId(),
+ .withAnimation = false};
+
+ Napi::Function callback = Napi::Function::New(ability->env(), [](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ LOGW("startAbility failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = QNapi::get<Napi::Object>(info[0]);
+ int code = QNapi::get<int>(error, "code");
+ if (code != 0)
+ LOGW("startAbility failed, code is %{public}d, message is %{public}s", code, error.Get("message").ToString().Utf8Value().c_str());
+ });
+ context->startAbility(startWant, options, callback);
+ });
+ auto status = feature.wait_for(std::chrono::seconds(3));
+ if (status == std::future_status::timeout) {
+ qCWarning(openharmonyQPA) << "wait for create top window time out";
+ return nullptr;
+ }
+ window = feature.get();
+ m_topWindowPromise.reset();
+ }
+ if (window != nullptr) {
+ platformWindow->nativeWindowNode()->setOwnerWindow(window);
+ window->attachPlatformWindow(platformWindow);
+ }
+ return window;
+}
+
+void QOhNativeWindowManager::handleTopWindowCreated(QJsAbility *ability)
+{
+ QJsWindowProxy *proxy = nullptr;
+ QOhNativeWindow *window = nullptr;
+ switch (ability->abilityType()) {
+ case QJsAbility::UIAbility: {
+ QJsContext *context = ability->context();
+ proxy = context->windowStage();
+ window = new QOhNormalWindow();
+ }
+ break;
+ case QJsAbility::UIExtensionAbility:
+ proxy = dynamic_cast<QJsUIExtensionAbility *>(ability)->session();
+ window = new QOhExtensionWindow();
+ break;
+ default:
+ break;
+ }
+ Q_ASSERT(proxy != nullptr);
+ Q_ASSERT(window != nullptr);
+ bool result = window->attachNativeWindow(proxy->mainwindow());
+ Q_ASSERT(result);
+ window->attachAbility(ability);
+ m_windows << window;
+ window->loadContent(ability->name());
+ if (m_topWindowPromise) {
+ m_topWindowPromise->set_value(window);
+ } else {
+ m_firstTopWindow = window;
+ }
+ if (qApp && (ability->abilityType() == QJsAbility::UIExtensionAbility))
+ Q_EMIT qApp->newNativeWindowCreated(ability->name());
+}
+
+void QOhNativeWindowManager::handleTopWindowDestroyed(const QString &name)
+{
+ QOhNativeWindow *window = qNativeWindowManager->window(name);
+ if (window) {
+ window->setDestroyed();
+ }
+}
+
+void QOhNativeWindowManager::setWindowXComponent(OH_NativeXComponent *component)
+{
+ QOhNativeWindow *w = window(getName(component));
+ if (w != nullptr) {
+ w->attachRootComponent(component);
+ }
+}
+
+QString QOhNativeWindowManager::getName(OH_NativeXComponent *component)
+{
+ char id[OH_XCOMPONENT_ID_LEN_MAX + 1] = { };
+ uint64_t id_length = OH_XCOMPONENT_ID_LEN_MAX + 1;
+ OH_NativeXComponent_GetXComponentId(component, id, &id_length);
+ QString name = QString::fromLatin1(id);
+ return name;
+}
+
+void QOhNativeWindowManager::init(Napi::Env env, Napi::Object exports)
+{
+ Napi::Object exportInstance = exports.Get(OH_NATIVE_XCOMPONENT_OBJ).As<Napi::Object>();
+ if (exportInstance.IsNull() ||
+ exportInstance.IsUndefined()) {
+ return;
+ }
+
+ OH_NativeXComponent *nativeXComponent = Napi::ObjectWrap<OH_NativeXComponent>::Unwrap(exportInstance);
+ if (nullptr == nativeXComponent) {
+ LOGE("unwrap OH_NATIVE_XCOMPONENT_OBJ failed.");
+ return;
+ }
+
+ qNativeWindowManager->setWindowXComponent(nativeXComponent);
+}
+
+QString QOhNativeWindowManager::getAbilityName()
+{
+ if (m_windows.isEmpty())
+ return QString("");
+
+ QJsAbility *ability = m_windows.first()->ability();
+ QJsWant *want = ability->launchWant();
+ return want->abilityName();
+}
+
+QString QOhNativeWindowManager::getModuleName()
+{
+ if (m_windows.isEmpty())
+ return QString("");
+
+ QJsAbility *ability = m_windows.first()->ability();
+ QJsWant *want = ability->launchWant();
+ return want->moduleName();
+}
+
+void QOhNativeWindowManager::show(QOhNativeWindow *window)
+{
+ if (window == nullptr) {
+ return;
+ }
+ QtOh::runOnJsUIThreadAndWait([window]{
+ QJsAbility *ability = window->ability();
+ if (!ability)
+ return;
+
+ QJsContext *context = ability->context();
+ QJsWant *want = ability->launchWant();
+ Want startWant = { want->bundleName(), want->moduleName(), want->abilityName(), {
+ "",
+ window->name()}
+ };
+ Napi::Function callback = Napi::Function::New(ability->env(), [](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ LOGW("startAbility failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0)
+ LOGW("startAbility failed, code is %{public}d, message is %{public}s", code, error.Get("message").ToString().Utf8Value().c_str());
+ });
+ dynamic_cast<QJsUIAbilityContext *>(context)->startAbility(startWant, callback);
+ });
+}
new file mode 100644
@@ -0,0 +1,70 @@
+#ifndef QOHNATIVEWINDOWMANAGER_H
+#define QOHNATIVEWINDOWMANAGER_H
+
+#include <QList>
+#include <QReadWriteLock>
+#include <future>
+#include <QWaitCondition>
+#include <QtCore/QJsObject>
+#include <napi/native_api.h>
+
+class QWindow;
+class QOhNativeWindow;
+struct OH_NativeXComponent;
+class QOhPlatformOpenGLWindow;
+class QJsAbility;
+class QJsContext;
+
+#define qNativeWindowManager QOhNativeWindowManager::instance()
+struct NativeResourceManager;
+
+class QOhNativeWindowManager
+{
+public:
+ QOhNativeWindowManager();
+ virtual ~QOhNativeWindowManager();
+
+ static QOhNativeWindowManager *instance();
+
+ QJsAbility *defaultAbility() const;
+ void destoryAllAbility();
+
+ NativeResourceManager *resourceManager();
+ Napi::Value jsResourceManager();
+
+ QJsContext *context(QWindow *window = nullptr) const;
+
+ QOhNativeWindow *createNativeWindow(QOhPlatformOpenGLWindow *platformWindow, const QRect &rect);
+
+ QOhNativeWindow *focusWindow();
+
+ bool exist(QOhNativeWindow *window);
+
+ void destroyWindow(QOhNativeWindow *window);
+
+ static void init(Napi::Env env, Napi::Object exports);
+
+ QString getAbilityName();
+ QString getModuleName();
+
+ void show(QOhNativeWindow *window);
+
+ QOhNativeWindow *window(const QString &name) const;
+ void handleTopWindowCreated(QJsAbility *ability);
+ void handleTopWindowDestroyed(const QString &name);
+ const QList<QJsAbility *> &allAbilities() const;
+private:
+ void setWindowXComponent(OH_NativeXComponent *component);
+private:
+ static QString getName(OH_NativeXComponent *component);
+ QOhNativeWindow *find(std::function<bool (QOhNativeWindow *)> function) const;
+ QOhNativeWindow *createSubWindow(QOhPlatformOpenGLWindow *platformWindow);
+ QOhNativeWindow *createTopWindow(QOhPlatformOpenGLWindow *platformWindow, const QRect &rect);
+private:
+ QList<QOhNativeWindow *> m_windows;
+ QOhNativeWindow *m_firstTopWindow;
+ std::shared_ptr<std::promise<QOhNativeWindow *>> m_topWindowPromise;
+ mutable QReadWriteLock m_windowsLock;
+};
+
+#endif // QOHNATIVEWINDOWMANAGER_H
new file mode 100644
@@ -0,0 +1,863 @@
+#include <QTimer>
+#include <QCloseEvent>
+#include <QGuiApplication>
+#include <qpa/qplatformscreen.h>
+#include <private/qohhiappevent_p.h>
+
+#include <qnapi.h>
+#include "qohkeys.h"
+#include "qohmain.h"
+#include "qjscontext.h"
+#include "qjswindowstage.h"
+#include "qloggingcategory.h"
+#include "qohevent.h"
+#include "qjsability.h"
+#include "qjsmodule.h"
+#include "qohwindowcontext.h"
+#include "qohnormalwindow.h"
+#include "qoheventdispatcher.h"
+#include "qohdisplay.h"
+#include "qohplatformscreen.h"
+#include "qohplatformopenglwindow.h"
+#include "qjsability.h"
+#include "qopenharmonydefines.h"
+#include "qohdisplaymanager.h"
+#include "qohnativewindowmanager.h"
+#include "qohjsonlistener.h"
+#include "qjsabilityinfo.h"
+#include "qjsuiabilitycontext.h"
+#include "qohplatforminputcontext.h"
+#include "qohplatformcursor.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <qpa/qplatformcursor.h>
+
+QOhNormalWindow::QOhNormalWindow(QOhNativeWindow *parent)
+ : QOhNativeWindow(parent)
+{
+
+}
+
+QOhNormalWindow::~QOhNormalWindow()
+{
+
+}
+
+QOhNormalWindow::WindowProperties QOhNormalWindow::getWindowProperties(const Napi::Object &window)
+{
+ static const QStringList windowPropertiesKeys = { "id", "windowRect", "drawableRect", "isFullScreen","focusable", "displayId"
+ /* 未使用的属性 */
+ #if 0
+ , "type", "isLayoutFullScreen",
+ "touchable", "brightness", "isKeepScreenOn",
+ "isPrivacyMode", "isTransparent"
+ #endif
+ };
+ return QtOh::runOnJsUIThreadWithResult([window] {
+ QJsObject obj(window);
+ Napi::Object ret = obj.call("getWindowProperties").As<Napi::Object>();
+ if (ret.IsEmpty() || ret.IsNull())
+ return WindowProperties{};
+
+ QVariantList values;
+ values.append(QNapi::get<int>(ret, windowPropertiesKeys[0]));
+ values.append(QtOh::jsRect2QRect(QNapi::get<Napi::Object>(ret, windowPropertiesKeys[1])));
+ values.append(QtOh::jsRect2QRect(QNapi::get<Napi::Object>(ret, windowPropertiesKeys[2])));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[3]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[4]));
+ values.append(QNapi::get<int>(ret, windowPropertiesKeys[5]));
+ /* 未使用的属性 */
+#if 0
+ values.append(QNapi::get<int>(ret, windowPropertiesKeys[6]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[7]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[8]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[9]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[10]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[11]));
+ values.append(QNapi::get<bool>(ret, windowPropertiesKeys[12]));
+#endif
+ WindowProperties windowProperties;
+ windowProperties.update(values);
+ return windowProperties;
+ });
+}
+
+QOhNativeWindow::WindowType QOhNormalWindow::nativeWindowType() const
+{
+ return QOhNativeWindow::NORMAL_WINDOW;
+}
+
+void QOhNormalWindow::attachAbility(QJsAbility *ability)
+{
+ QOhNativeWindow::attachAbility(ability);
+ safeExec([this, ability] {
+ if (m_ability != nullptr) {
+ if (15 <= QtOh::apiVersion() && !QtOh::isTabletDevice() && !QtOh::isPhoneDevice()) {
+ /* NOTE 该接口需要配置PREPARE_APP_TERMINATE权限(module.json5) */
+ Napi::Function prepareToTerminateAsync = Napi::Function::New(env(), [this, ability](const Napi::CallbackInfo& info){
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+ if (!m_window || QGuiApplicationPrivate::instance()->isWindowBlocked(m_window->window())) {
+ if (m_window)
+ qCWarning(openharmonyQPA) << m_window->window() << "is blocked";
+ deferred.Resolve(Napi::Boolean::New(deferred.Env(), true));
+ return deferred.Promise();
+ }
+ QString name = ability->name();
+ const QString &tag = QString("pt_begin_%1").arg(name);
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag(tag.toStdString());
+ qCWarning(openharmonyQPA) << "arkui ability prepareToTerminateAsync: " << m_window->window();
+#if 1
+ QWindowSystemInterface::handleCloseEvent(m_window->window(), QCloseEvent::AbilityClose, [name, deferred](bool accept){
+ QtOh::runOnJsUIThreadNoWait([name, deferred, accept] {
+ const QString &tag = QString("pt_end_%1").arg(name);
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag(tag.toStdString());
+
+ /* NOTE 鸿蒙窗口关闭和Qt-Accept相反 */
+ deferred.Resolve(Napi::Boolean::New(deferred.Env(), !accept));
+ });
+ });
+#else
+ QWindowSystemInterface::handleApplicationTermination([name, deferred](bool accept){
+ QtOh::runOnJsUIThreadNoWait([name, deferred, accept]{
+ const QString &tag = QString("pt_end_%1").arg(name);
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag(tag.toStdString());
+ deferred.Resolve(Napi::Boolean::New(deferred.Env(), !accept));
+ });
+ });
+#endif
+ return deferred.Promise();
+ });
+ m_ability->set("onPrepareToTerminateAsync", prepareToTerminateAsync);
+ }
+ /* NOTE 该接口需要配置PREPARE_APP_TERMINATE权限(module.json5) */
+ /* NOTE PAD设备需要使用同步接口 */
+ Napi::Function closeAbilityListener = Napi::Function::New(env(), [this](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info);
+ //m_isWindowDestroyed.storeRelease(true);
+ QtOh::runOnQtMainThread(&QOhNormalWindow::handleWindowEvent, this, QtOh::WindowState::WINDOW_DESTROYED);
+ return Napi::Boolean::New(env(), true);
+ });
+
+ m_ability->set("onPrepareToTerminate", closeAbilityListener);
+ }
+ });
+}
+
+int32_t QOhNormalWindow::id() const
+{
+ if (m_windowId != -1)
+ return m_windowId;
+
+ m_windowId = properties().id();
+ return m_windowId;
+}
+
+void QOhNormalWindow::setWindowLimits(WindowLimits &limits, bool force)
+{
+ qCInfo(openharmonyQPA) << "setWindowLimits" << force;
+
+ if (m_window != nullptr && m_window->hasFrame()) {
+ QMargins m = frameMargins();
+ QSize off(m.left() + m.right(), m.top() + m.bottom());
+ limits += off;
+ }
+ // limits.intersect(getWindowLimits());
+
+ safeExec([this, &limits, force] {
+#if OHOS_SDK_VERSION >= 15
+ Napi::HandleScope scope(env());
+ Napi::Value retValue = force
+ ? call("setWindowLimits", {limits.toObject(env()), Napi::Boolean::New(env(),force)})
+ : call("setWindowLimits", {limits.toObject(env())});
+ if (retValue.IsEmpty() || retValue.IsNull())
+ return;
+
+ Napi::Promise ret = retValue.As<Napi::Promise>();
+#else
+ Q_UNUSED(force);
+ Napi::Promise ret = call("setWindowLimits", {limits.toObject(env())}).As<Napi::Promise>();
+#endif
+ QJsPromise promise(ret);
+ promise.onThen([](const Napi::CallbackInfo &info) {
+ qCInfo(openharmonyQPA) << "Actual window limit:" << WindowLimits(info[0].ToObject());
+ });
+ });
+}
+
+QOhNativeWindow::WindowLimits QOhNormalWindow::getWindowLimits()
+{
+ return safeExec([this] {
+ return WindowLimits(Napi::Object(env(), call("getWindowLimits").As<Napi::Object>()));
+ });
+}
+
+bool QOhNormalWindow::isFocused()
+{
+ return safeExec([this] { return call<bool>("isFocused"); });
+}
+
+bool QOhNormalWindow::setGeometry(const QRect &rectIn)
+{
+ QRect actualRect(rectIn);
+ if (m_isSplashWindow && m_parentNativeWindow) {
+ /* 限制启动窗口大小 */
+ QSize miniSize = m_parentNativeWindow->geometry().size();
+ QSize maxSize = QSize(0xFFF, 0xFFF);
+ auto displayId = m_parentNativeWindow->displayId();
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (platformScreen) {
+ actualRect.translate(platformScreen->geometry().topLeft());
+ maxSize = platformScreen->availableGeometry().size();
+ }
+ actualRect.setSize(QSize(qBound(miniSize.width(), rectIn.width(), maxSize.width()), \
+ qBound(miniSize.height(), rectIn.height(), maxSize.height())));
+ actualRect.moveCenter(m_parentNativeWindow->geometry().center());
+ if (actualRect != rectIn) {
+ /* 实际窗口大小与上层不同,在此处更新 */
+ QOhPlatformOpenGLWindow *platformWindow = dynamic_cast<QOhPlatformOpenGLWindow *>(m_window);
+ if (platformWindow)
+ platformWindow->setNativeGeometry(actualRect);
+ QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(m_window->window(), actualRect);
+ }
+ }
+
+ /* 如果是第一次设置,查询窗口是否保存了坐标尺寸 */
+ if (m_firstSetGeometry) {
+ m_firstSetGeometry = false;
+ if (handleFirstSetGeometry())
+ return true;
+ }
+
+ return safeExecWithPromise<bool>([this, actualRect](auto p) {
+ QRect jsRect = adjustRect(m_window->window(), actualRect, frameMargins());
+ qCInfo(openharmonyQPA) << "QOhNormalWindow::setGeometry set window geometry: " << m_name << jsRect;
+ QtOh::WindowStatusType status = windowStatus();
+ if ( QtOh::WindowStatusType::FULL_SCREEN == status ||
+ QtOh::WindowStatusType::MAXIMIZE == status ||
+ QtOh::WindowStatusType::SPLIT_SCREEN == status) {
+ call("recover");
+ }
+ Napi::Promise result = call("resize", { Napi::Number::New(env(), MAX(1, jsRect.width())), Napi::Number::New(env(), MAX(1, jsRect.height())) }).As<Napi::Promise>();
+ QJsPromise promise(result);
+ promise.onThen([this, jsRect, p](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info)
+ if (QtOh::apiVersion() > 13) {
+ QOhPlatformScreen *current = dynamic_cast<QOhPlatformScreen*>(m_window->screen());
+ Q_ASSERT(current != nullptr);
+ QOhPlatformScreen *ps = current;
+ auto *targetPlatformScreen = QOhDisplayManager::instance()->screenAt(jsRect.topLeft());
+ if (!targetPlatformScreen) {
+ targetPlatformScreen = QOhDisplayManager::instance()->screenAt(jsRect.topLeft());
+ }
+ if (targetPlatformScreen) {
+ ps = targetPlatformScreen;
+ }
+ auto x = jsRect.x() - ps->geometry().x();
+ auto y = jsRect.y() - ps->geometry().y();
+ auto displayId = ps->displayId();
+ if (ps != current) {
+ //change displayId will lead to displayIdChange
+ QWindowSystemInterface::handleWindowScreenChanged(m_window->window(), ps->screen());
+ qCInfo(openharmonyQPA) << "switch window display: " << ps;
+ }
+ auto optional = Napi::Object::New(env());
+ optional.Set("displayId", Napi::Number::New(env(), displayId));
+ call("moveWindowToGlobal", { Napi::Number::New(env(), x), Napi::Number::New(env(), y), optional });
+ p->set_value(true);
+ } else {
+ call("moveWindowTo", { Napi::Number::New(env(), jsRect.x()), Napi::Number::New(env(), jsRect.y()) });
+ p->set_value(true);
+ }
+ }).onCatch([p](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info);
+ Napi::Object error = info[0].As<Napi::Object>();
+ if (!error.IsNull()) {
+ Napi::Number code = error.Get("code").ToNumber();
+ Napi::String message = error.Get("message").ToString();
+ LOGW("set window geometry failed code: %{public}d message: %{public}s",
+ code.Int32Value(),
+ message.Utf8Value().c_str());
+ }
+ p->set_value(false);
+ });
+ });
+}
+
+QRect QOhNormalWindow::geometry() const
+{
+ return properties().windowRect();
+}
+
+QRect QOhNormalWindow::normalGeometry() const
+{
+ return m_normalGeometry;
+}
+
+QMargins QOhNormalWindow::frameMargins() const
+{
+ QMargins m;
+ QtOh::WindowStatusType winStatus = windowStatus();
+ if ((QtOh::isTabletDevice() || QtOh::isPhoneDevice()) && !QtOh::isFreeWindowEnable()) {
+ if (QtOh::WindowStatusType::FLOATING == winStatus) {
+ m = QMargins(0, titleHeight(), 0, 0);
+ } else if (QtOh::WindowStatusType::MAXIMIZE == winStatus) {
+ QOhNativeWindow::AvoidArea area = windowAvoidArea(AvoidAreaType::TYPE_SYSTEM);
+ if (area.visible)
+ m.setTop(qMax(m.top(), area.topRect.height()));
+
+ area = windowAvoidArea(AvoidAreaType::TYPE_NAVIGATION_INDICATOR);
+ if (area.visible)
+ m.setBottom(qMax(m.bottom(), area.bottomRect.height()));
+ }
+ } else if ((QtOh::isTabletDevice() || QtOh::isPhoneDevice()) && QtOh::isFreeWindowEnable()) {
+ if (QtOh::WindowStatusType::FULL_SCREEN == winStatus) {
+ return m;
+ }
+ m = QMargins(0, titleHeight(), 0, 0);
+ if (QtOh::WindowStatusType::MAXIMIZE == winStatus) {
+ /* pad 自由多窗模式,最大化时 显示区域要减去导航栏高度的规避区域 */
+ QOhNativeWindow::AvoidArea area = windowAvoidArea(AvoidAreaType::TYPE_NAVIGATION_INDICATOR);
+ if (area.visible)
+ m.setBottom(m.bottom() + area.bottomRect.height());
+ }
+ } else {
+ if ((QtOh::WindowStatusType::FULL_SCREEN == winStatus)) {
+ m = QMargins();
+ } else {
+ m = QMargins(0, titleHeight(), 0, 0);
+ }
+ }
+ return m;
+}
+
+void QOhNormalWindow::requestFocus()
+{
+ safeExec([this] {
+ if (this->isFocused())
+ return;
+ bool focusable = properties().focusable();
+
+ if (!focusable) {
+ m_focusable = true;
+ call("setWindowFocusable", {Napi::Boolean::New(env(), true)});
+ }
+
+ int preActiveId = 0;
+ if (QWindow *w = qApp->focusWindow()) {
+ if (QOhPlatformOpenGLWindow *pw = dynamic_cast<QOhPlatformOpenGLWindow*>(w->handle()))
+ preActiveId = pw->nativeWindowId();
+ }
+ if (m_windowModule.isNull())
+ m_windowModule.reset(new QJsModule("@ohos.window"));
+
+ m_windowModule->call("shiftAppWindowFocus", preActiveId, this->id());
+ });
+}
+
+QtOh::WindowStatusType QOhNormalWindow::windowStatus() const
+{
+ return safeExec([this] {
+ //NOTE:可能还有状态更新操作未完成(如连续showMinimized,showNormal),手机设备不支持getWindowStatus接口
+ // int32_t status = call("getWindowStatus").ToNumber();
+ int32_t status = m_expectedWindowStatus.loadAcquire();
+ QtOh::WindowStatusType result = static_cast<QtOh::WindowStatusType>(status);
+
+ if (!QtOh::isSupportFreeWindow() && isTopWindow() && QtOh::WindowStatusType::FLOATING == result) {
+ result = QtOh::WindowStatusType::FULL_SCREEN;
+ }
+
+ if (result == QtOh::WindowStatusType::FULL_SCREEN) {
+ bool immersiveModeEnabledState = call<bool>("getImmersiveModeEnabledState", {});
+ if (!immersiveModeEnabledState)
+ result = QtOh::WindowStatusType::MAXIMIZE;
+ }
+ return result;
+ });
+}
+
+QtOh::WindowStatusType QOhNormalWindow::windowStatusFromSystem() const
+{
+ return safeExec([this] {
+ int32_t status = call("getWindowStatus").ToNumber();
+ QtOh::WindowStatusType result = QtOh::WindowStatusType(status);
+ if (result == QtOh::WindowStatusType::FULL_SCREEN) {
+ bool immersiveModeEnabledState = call<bool>("getImmersiveModeEnabledState", {});
+ if (!immersiveModeEnabledState)
+ result = QtOh::WindowStatusType::MAXIMIZE;
+ }
+ return result;
+ });
+}
+
+void QOhNormalWindow::stopListener()
+{
+ QOhNativeWindow::stopListener();
+#if OHOS_SDK_VERSION >= 15
+ auto winId = id();
+// OH_NativeWindowManager_UnregisterTouchEventFilter(winId);
+ OH_NativeWindowManager_UnregisterMouseEventFilter(winId);
+#endif
+}
+
+void QOhNormalWindow::startListener()
+{
+ m_displayId = properties().displayId();
+ asynSafeExec([&] {
+ m_listeners << new QOhJsOnListener(this, "windowRectChange", [this](const Napi::CallbackInfo& info) {
+ if (isDestroyed())
+ return;
+ Napi::Object opt = QNapi::getFirst<Napi::Object>(info);
+ QRect qtRect(QtOh::jsRect2QRect(QNapi::get<Napi::Object>(opt, "rect")));
+ auto changeReason = QNapi::get<WindowChangeReason>(opt, "reason");
+ QMargins m = frameMargins();
+ if (changeReason != WindowChangeReason::MAXIMIZE)
+ m_normalGeometry = qtRect - m;
+ bool sizeChanged = qtRect.size() != m_lastReportedGeometry.size();
+ bool positionChanged = qtRect.topLeft() != m_lastReportedGeometry.topLeft();
+ // 如果只是坐标变化,发送geometryChanged event,如果尺寸也变化,xcompoent的尺寸也会发生
+ // 变化,在xcompoent的surfaceChanged中处理geometryChanged
+ bool send = (positionChanged && !sizeChanged) || m_lastReportedGeometry.isEmpty();
+ m_lastReportedGeometry = qtRect;
+ LOGI("window rect changed windowRectChange-(%{public}p): Rect(%{public}d, %{public}d %{public}dX%{public}d)",
+ m_window ? m_window->window() : nullptr, qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+ if (m_window && send) {
+ handleGeometryChanged(qtRect, m);
+ }
+ });
+
+ m_listeners << new QOhJsOnListener(this, "windowStatusChange", [this](const Napi::CallbackInfo& info) {
+ if (isDestroyed() || m_isSplashWindow)
+ return ;
+ auto event = QNapi::getFirst<QtOh::WindowStatusType>(info);
+ if (event == QtOh::WindowStatusType::MAXIMIZE || event == QtOh::WindowStatusType::FULL_SCREEN) {
+ bool immersiveModeEnabledState = QNapi::get<bool>(call("getImmersiveModeEnabledState"));
+ if (immersiveModeEnabledState)
+ event = QtOh::WindowStatusType::FULL_SCREEN;
+ else
+ event = QtOh::WindowStatusType::MAXIMIZE;
+ }
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(event));
+ QtOh::runOnQtMainThread(&QOhNormalWindow::handleWindowStatusEvent, this, event);
+ });
+
+ if (QtOh::apiVersion() > 13) {
+ m_listeners << new QOhJsOnListener(this, "displayIdChange", [this](const Napi::CallbackInfo& info) {
+ if (isDestroyed())
+ return;
+
+ // 8指键盘弹出关闭会时触发的窗口位置变换在坐标一样的时候不会发windowrectchange,
+ // 只会发一个displayidchange, 在此更新窗口尺寸
+ handleGeometryChanged();
+
+ uint32_t displayId = QNapi::getFirst<uint32_t>(info);
+ QtOh::runOnQtMainThread(&QOhNormalWindow::checkForScreenChanged, this, displayId);
+ });
+ }
+#if OHOS_SDK_VERSION >= 15
+ m_listeners << new QOhJsOnListener(this, "windowWillClose", [this](const Napi::CallbackInfo &info) {
+ QString name = this->name();
+ const QString &tag = QString("wc_begin_%1").arg(name);
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag(tag.toStdString());
+
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(info.Env());
+ QWindowSystemInterface::handleCloseEvent(m_window->window(), QCloseEvent::WindowClose, [name, deferred](bool accept){
+ QtOh::runOnJsUIThreadNoWait([name, deferred, accept]{
+ const QString &tag = QString("wc_end_%1").arg(name);
+ QtOhosWatchdog::QOhHiAppEvent::instance()->writeDataTag(tag.toStdString());
+
+ /* NOTE 鸿蒙窗口关闭和Qt-Accept相反 */
+ deferred.Resolve(Napi::Boolean::New(deferred.Env(), !accept));
+ });
+ });
+ return deferred.Promise();
+ });
+
+ m_listeners << new QOhJsOnListener(this, "windowTitleButtonRectChange", [this](const Napi::CallbackInfo& info){
+ m_titleButtonRect = QtOh::jsRect2QRect(info[0].As<Napi::Object>());
+ });
+#endif
+
+ m_listeners << new QOhJsOnListener(this, "windowEvent", [this](const Napi::CallbackInfo& info) {
+ if (isDestroyed())
+ return;
+
+ QtOh::WindowState type = QNapi::getFirst<QtOh::WindowState>(info);
+ if (type == QtOh::WindowState::WINDOW_DESTROYED)
+ setDestroyed();
+
+ if (type == QtOh::WindowState::WINDOW_SHOWN && m_handleGeometryChangedPending) {
+ handleGeometryChanged();
+ }
+ QtOh::runOnQtMainThread(&QOhNormalWindow::handleWindowEvent, this, type);
+ });
+
+ if (!isTopWindow()) {
+ m_listeners << new QOhJsOnListener(this, "subWindowClose", [this](const Napi::CallbackInfo& info) {
+ Q_UNUSED(info)
+ QtOh::runOnQtMainThread(&QOhNormalWindow::handleWindowEvent, this, QtOh::WindowState::WINDOW_DESTROYED);
+ return QNapi::create(true);
+ });
+ }
+
+ QOhNativeWindow::startListener();
+ });
+
+#if OHOS_SDK_VERSION >= 15
+ // NOTE:openharmony5.0.3报错2000
+ if (!QtOh::isOpenHarmonyDevice()) {
+ registerMouseAndTouchEventFilter();
+ }
+#endif
+}
+
+bool QOhNormalWindow::canShowWindow() const
+{
+ if (m_firstShowWindow) {
+ m_firstShowWindow = false;
+ if (isWindowRectAutoSave())
+ return false;
+ }
+ return true;
+}
+
+void QOhNormalWindow::reportWindowGeometry(const QRect &rectIn)
+{
+ /* 获取windowRect传给Qt */
+ safeExec([this, rectIn] { handleGeometryChanged(rectIn); });
+}
+
+bool QOhNormalWindow::loadContent(const QString &name)
+{
+ QOhNativeWindow::loadContent(name);
+ return safeExec([this](const QString &name) {
+ QJsAbility *ability = this->ability();
+ Q_ASSERT(ability != nullptr);
+ QJsObject store(ability->newLocalStorage().As<Napi::Object>());
+ store.call("setOrCreate", "idName", name);
+ call("loadContent", { Napi::String::New(env(), "pages/Index"), store.object()});
+ return true;
+ }, name);
+}
+
+QOhNormalWindow::WindowProperties QOhNormalWindow::properties() const
+{
+ return safeExec([this] { return getWindowProperties(object()); });
+}
+
+int QOhNormalWindow::titleHeight() const
+{
+ if (m_window == nullptr || !m_window->hasFrame() || (!QtOh::isSupportFreeWindow() && isTopWindow()))
+ return 0;
+ return safeExec([this] {
+ Napi::Value value = call("getWindowDecorHeight");
+ int height = value.IsEmpty() ? 0 : value.ToNumber();
+ if(!m_window)
+ return qreal(0.0);
+ return QtOh::densityPixels(m_window->screen()) * height;
+ });
+}
+
+QRect QOhNormalWindow::titleButtonRect() const
+{
+ return safeExec([this] {
+ Napi::Object result = call("getTitleButtonRect").As<Napi::Object>();
+ return QtOh::jsRect2QRect(result);
+ });
+}
+
+QOhNativeWindow::HitTest QOhNormalWindow::hitTest(const QPoint &point, qreal scale) const
+{
+ QRect tbr = m_titleButtonRect;
+ // titleButtonRect相对于窗口的右上角坐标,转换成全局坐标
+ QRect geo = m_lastReportedGeometry;
+ int tbrWidth = tbr.width() * scale;
+ int tbrHeight = tbr.height() * scale;
+ int x = geo.x() + geo.width() - tbrWidth;
+ int y = geo.y();
+ static const int marginvp = 18;
+ qreal marginpx = marginvp * scale;
+ QRect systemButtonRect = QRect(x + marginpx, y, qMax(0, static_cast<int>(tbrWidth - marginpx * 2)), tbrHeight);
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(m_displayId);
+ if (platformScreen) {
+ systemButtonRect.translate(platformScreen->geometry().topLeft());
+ }
+ if (systemButtonRect.contains(point))
+ return SystemButton;
+ return Client;
+}
+
+bool QOhNormalWindow::isWindowRectAutoSave() const
+{
+ if (!isDestroyed() && m_ability) {
+ if (QJsContext *context = m_ability->context()) {
+ if (QJsWindowStage *ws = context->windowStage()) {
+ return ws->isWindowRectAutoSave();
+ }
+ }
+ }
+ return false;
+}
+
+bool QOhNormalWindow::handleFirstSetGeometry()
+{
+ if (!isTopWindow())
+ return false;
+ if (!isDestroyed() && !isWindowRectAutoSave()) {
+ if (m_ability != nullptr) {
+ if (QJsUIAbilityContext *context = dynamic_cast<QJsUIAbilityContext *>(m_ability->context())) {
+ QJsAbilityInfo *info = context->abilityInfo();
+ if (!info->isInitGeometrySetted())
+ return false;
+ }
+ }
+ return false;
+ }
+ reportWindowGeometry();
+ QtOh::WindowStatusType type = windowStatusFromSystem();
+ m_expectedWindowStatus.storeRelease(static_cast<int32_t>(type));
+ handleWindowStatusEvent(type);
+ return true;
+}
+
+void QOhNormalWindow::handleGeometryChanged(const QRect &rectIn, const QMargins &margins)
+{
+ QMargins m = margins;
+ if (m.isNull())
+ m = frameMargins();
+ QRect rect = rectIn;
+ if (!rect.isValid()) {
+ if (isVisible()) {
+ rect = geometry();
+ } else {
+ // 窗口在隐藏时获取不到窗口大小
+ m_handleGeometryChangedPending = true;
+ return;
+ }
+ }
+ if (m_handleGeometryChangedPending) {
+ m_handleGeometryChangedPending = false;
+ m_lastReportedGeometry = rect;
+ }
+ if (!rect.isValid())
+ return;
+
+ auto displayId = properties().displayId();
+ rect = rect - m;
+
+ /* 由屏幕局部坐标系,换算成虚拟屏幕坐标系 */
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (platformScreen) {
+ rect.translate(platformScreen->geometry().topLeft());
+ }
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GeometryChangeEvent> e(new QtOh::GeometryChangeEvent());
+ e->rect = rect;
+// e->reason = (int)reason;
+ e->window = m_window->window();
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+#if OHOS_SDK_VERSION >= 15
+void QOhNormalWindow::registerMouseAndTouchEventFilter()
+{
+ auto winId = id();
+ if (winId == 0)
+ return;
+ auto errCode = OH_NativeWindowManager_RegisterMouseEventFilter(winId, &QOhNormalWindow::windowMouseEventFilter);
+ if (errCode != WindowManager_ErrorCode::OK) {
+ LOGW("%s registerMouseEventFilter failed: %{public}d", Q_FUNC_INFO, errCode);
+ }
+ errCode = OH_NativeWindowManager_RegisterTouchEventFilter(winId,
+ &QOhNormalWindow::windowTouchEventFilter);
+ if (errCode != WindowManager_ErrorCode::OK) {
+ LOGW("%s registerTouchEventFilter failed: %{public}d", Q_FUNC_INFO, errCode);
+ }
+}
+
+bool QOhNormalWindow::windowMouseEventFilter(Input_MouseEvent *mouseEvent)
+{
+ auto winId = OH_Input_GetMouseEventWindowId(mouseEvent);
+ auto action = (Input_MouseEventAction)OH_Input_GetMouseEventAction(mouseEvent);
+ auto btn = (Input_MouseEventButton)OH_Input_GetMouseEventButton(mouseEvent);
+ auto displayX = OH_Input_GetMouseEventDisplayX(mouseEvent);
+ auto displayY = OH_Input_GetMouseEventDisplayY(mouseEvent);
+ auto displayId = OH_Input_GetMouseEventDisplayId(mouseEvent);
+
+ QtOh::runOnQtMainThread([displayId, winId, action, btn, displayX, displayY]{
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ auto screenTopLeft = QPoint();
+ if (platformScreen) {
+ screenTopLeft = platformScreen->geometry().topLeft();
+ }
+
+ QOhPlatformWindow *pw = QOhWindowContext::getByWindowId(winId);
+ if (!pw) {
+ return;
+ }
+
+ auto globalPos = QPoint { displayX, displayY };
+ globalPos += screenTopLeft;
+ bool isClientArea = pw->geometry().contains(globalPos);
+
+ static bool isClientAreaPressed = false;
+ /* 客户区域内点击后鼠标移动期间,鼠标移动到非客户区内node也会触发鼠标移动事件 */
+ if (MOUSE_ACTION_BUTTON_DOWN == action && isClientArea)
+ isClientAreaPressed = true;
+
+ if (isClientAreaPressed) {
+ if (MOUSE_ACTION_BUTTON_UP == action)
+ isClientAreaPressed = false;
+ return;
+ }
+
+ static bool enterClient = false;
+ if (isClientArea) {
+ // 客户区点击使用node事件处理,在此处只处理非客户区的鼠标事件
+ enterClient = true;
+ return;
+ }
+
+ if (enterClient && !QOhWindowContext::underMouseWindow()) {
+ QOhPlatformCursor::setDefaultCursor(pw->nativeWindowId());
+ enterClient = false;
+ }
+
+ QSharedPointer<QtOh::MouseEvent> event(new QtOh::MouseEvent());
+ switch (action) {
+ //QDocWidge拖入hover效果需要MouseMove事件
+ case MOUSE_ACTION_MOVE:
+ event->mouseEventType = QEvent::NonClientAreaMouseMove;
+ break;
+ case MOUSE_ACTION_BUTTON_DOWN:
+ event->mouseEventType = QEvent::NonClientAreaMouseButtonPress;
+ break;
+ case MOUSE_ACTION_BUTTON_UP:
+ event->mouseEventType = QEvent::NonClientAreaMouseButtonRelease;
+ break;
+ default:
+ return;
+ }
+
+ switch (btn) {
+ case MOUSE_BUTTON_NONE:
+ event->mouseButton = Qt::NoButton;
+ break;
+ case MOUSE_BUTTON_LEFT:
+ event->mouseButton = Qt::LeftButton;
+ break;
+ case MOUSE_BUTTON_MIDDLE:
+ event->mouseButton = Qt::MiddleButton;
+ break;
+ case MOUSE_BUTTON_RIGHT:
+ event->mouseButton = Qt::RightButton;
+ break;
+ case MOUSE_BUTTON_FORWARD:
+ event->mouseButton = Qt::ForwardButton;
+ break;
+ case MOUSE_BUTTON_BACK:
+ event->mouseButton = Qt::BackButton;
+ break;
+ }
+ QWindow *window = pw->window();
+ while (window && (window->flags() & Qt::WindowTransparentForInput))
+ window = window->parent();
+ if (!window)
+ return;
+ auto localPos = window->handle()->mapFromGlobal(globalPos);
+ event->window = pw->window();
+ event->xcPos = QPointF(localPos);
+ event->scPos = QPointF(globalPos);
+ event->keyboardModifiers = QOhKeys::keyboardModifiers();
+
+ QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance();
+ dispatcher->appendOhEvent(event);
+ });
+ return false;
+}
+
+bool QOhNormalWindow::windowTouchEventFilter(Input_TouchEvent *touchEvent)
+{
+ auto winId = OH_Input_GetTouchEventWindowId(touchEvent);
+ auto displayId = OH_Input_GetTouchEventDisplayId(touchEvent);
+ auto displayX= OH_Input_GetTouchEventDisplayX(touchEvent);
+ auto displayY = OH_Input_GetTouchEventDisplayY(touchEvent);
+ QtOh::runOnQtMainThread([displayId, winId, displayX, displayY]{
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ auto screenTopLeft = QPoint();
+ if (platformScreen) {
+ screenTopLeft = platformScreen->geometry().topLeft();
+ }
+
+ bool isNonClientArea = false;
+ QOhPlatformWindow *pw = QOhWindowContext::getByWindowId(winId);
+ if (!pw) {
+ return;
+ }
+ auto globalPos = QPoint { displayX, displayY };
+ globalPos += screenTopLeft;
+ isNonClientArea = !pw->geometry().contains(globalPos);
+ if (isNonClientArea) {
+ auto screens = QGuiApplication::screens();
+ for (int i = 0; i < screens.count(); ++i) {
+ if (QPlatformCursor *cursor = screens.at(i)->handle()->cursor())
+ cursor->setPos(QPoint(displayX, displayY));
+ }
+ }
+ });
+ return false;
+}
+
+#endif
+
+/**
+ * @brief class QOhNormalWindow::WindowProperties::WindowProperties Start
+ */
+#define safeGetValue(values, index) ((values).size() > (index) ? (values).at(index) : QVariant())
+QOhNormalWindow::WindowProperties::WindowProperties()
+{
+ // for (int i = 0; i < windowPropertiesKeys.size(); i++) {
+ // m_values.append(QVariant());
+ // }
+}
+
+void QOhNormalWindow::WindowProperties::update(const QVariantList &value)
+{
+ m_values = value;
+}
+
+int32_t QOhNormalWindow::WindowProperties::id()
+{
+ return safeGetValue(m_values, 0).toInt();
+}
+
+uint32_t QOhNormalWindow::WindowProperties::displayId()
+{
+ return safeGetValue(m_values, 5).toInt();
+}
+
+QRect QOhNormalWindow::WindowProperties::windowRect()
+{
+ return safeGetValue(m_values, 1).value<QRect>();
+}
+
+QRect QOhNormalWindow::WindowProperties::drawableRect()
+{
+ return safeGetValue(m_values, 2).value<QRect>();
+}
+
+bool QOhNormalWindow::WindowProperties::isFullScreen()
+{
+ return safeGetValue(m_values, 3).toBool();
+}
+
+bool QOhNormalWindow::WindowProperties::focusable()
+{
+ return safeGetValue(m_values, 4).toBool();
+}
+/**
+ * @brief class QOhNormalWindow::WindowProperties::WindowProperties End
+ */
new file mode 100644
@@ -0,0 +1,81 @@
+#ifndef QOHNORMALWINDOW_H
+#define QOHNORMALWINDOW_H
+
+#include "qohnativewindow.h"
+
+class QOhNormalWindow : public QOhNativeWindow
+{
+public:
+ class WindowProperties
+ {
+ public:
+ WindowProperties();
+ void update(const QVariantList &);
+ int32_t id();
+ uint32_t displayId();
+ QRect windowRect();
+ QRect drawableRect();
+ bool isFullScreen();
+ bool focusable();
+
+ private:
+ QVariantList m_values;
+ };
+ explicit QOhNormalWindow(QOhNativeWindow *parent = nullptr);
+ ~QOhNormalWindow();
+
+ static WindowProperties getWindowProperties(const Napi::Object &window);
+
+ WindowType nativeWindowType() const override;
+
+ void attachAbility(QJsAbility *ability) override;
+
+ int32_t id() const override;
+
+ void setWindowLimits(WindowLimits &, bool force = true) override;
+
+ WindowLimits getWindowLimits() override;
+
+ bool isFocused() override;
+
+ bool setGeometry(const QRect &rectIn) override;
+
+ QRect geometry() const override;
+
+ virtual QRect normalGeometry() const override;
+
+ QMargins frameMargins() const override;
+
+ void requestFocus() override;
+
+ QtOh::WindowStatusType windowStatus() const override;
+ QtOh::WindowStatusType windowStatusFromSystem() const;
+
+ void stopListener() override;
+ void startListener() override;
+ bool canShowWindow() const override;
+ QRect titleButtonRect() const override;
+ HitTest hitTest(const QPoint &point, qreal scale) const override;
+ void reportWindowGeometry(const QRect &rectIn = QRect()) override;
+ virtual bool loadContent(const QString &name) override;
+ WindowProperties properties() const;
+private:
+ int titleHeight() const;
+ bool isWindowRectAutoSave() const;
+ bool handleFirstSetGeometry();
+ void handleGeometryChanged(const QRect &rectIn = QRect(), const QMargins &margins = QMargins());
+#if OHOS_SDK_VERSION >= 15
+ void registerMouseAndTouchEventFilter();
+ static bool windowMouseEventFilter(Input_MouseEvent *mouseEvent);
+ static bool windowTouchEventFilter(Input_TouchEvent *touchEvent);
+#endif
+ QRect m_lastReportedGeometry;
+ QRect m_titleButtonRect;
+ QRect m_normalGeometry;
+ mutable int32_t m_windowId = -1;
+ mutable bool m_firstShowWindow = true;
+ bool m_firstSetGeometry = true;
+ bool m_handleGeometryChangedPending = false;
+};
+
+#endif // QOHNORMALWINDOW_H
new file mode 100644
@@ -0,0 +1,57 @@
+#ifndef QOHOBJECTHOLDER_H
+#define QOHOBJECTHOLDER_H
+
+#include <QtCore/qglobal.h>
+#include <functional>
+#include <utility>
+QT_BEGIN_NAMESPACE
+
+template<typename Type>
+class QOhObjectHolder
+{
+ using Deleter = std::function<void(Type**)>;
+public:
+ template<typename RetType, typename Creator, typename... Args,
+ typename = std::enable_if_t<!std::is_same_v<std::decay_t<Creator>, Type*>>>
+ QOhObjectHolder(Creator &&creator, RetType(*deleter)(Type*), Args&&... args)
+ : m_object(creator(std::forward<Args>(args)...))
+ , m_deleter(deleter ? [deleter](Type** obj) { deleter(*obj); *obj = nullptr; } : Deleter{})
+ {}
+
+ template<typename RetType, typename Creator, typename... Args,
+ typename = std::enable_if_t<!std::is_same_v<std::decay_t<Creator>, Type*>>>
+ QOhObjectHolder(Creator &&creator, RetType(*deleter)(Type**), Args&&... args)
+ : m_object(creator(std::forward<Args>(args)...))
+ , m_deleter(deleter ? std::function<void(Type**)>(deleter) : Deleter{})
+ {}
+
+ template<typename RetType = void>
+ QOhObjectHolder(Type *type, RetType(*deleter)(Type*) = nullptr)
+ : m_object(type)
+ , m_deleter(deleter ? [deleter](Type** obj) { deleter(*obj); *obj = nullptr; } : Deleter{})
+ {}
+
+ template<typename RetType = void,
+ typename = std::enable_if_t<!std::is_same_v<RetType(*)(Type**), nullptr_t>>>
+ QOhObjectHolder(Type *type, RetType(*deleter)(Type**))
+ : m_object(type)
+ , m_deleter(std::function<void(Type**)>(deleter))
+ {}
+
+ ~QOhObjectHolder() {
+ if (m_object && m_deleter) {
+ m_deleter(&m_object);
+ }
+ }
+
+ Type* object() const { return m_object; }
+ Type *take() { Type *ret = m_object; m_object = nullptr; return ret; }
+
+private:
+ Type* m_object = nullptr;
+ Deleter m_deleter;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHOBJECTHOLDER_H
new file mode 100644
@@ -0,0 +1,155 @@
+#include "qohplatformabilityctrl.h"
+#include "qohnativewindowmanager.h"
+#include "qjsuiabilitycontext.h"
+#include <qopenharmonydefines.h>
+#include "qohauxiliary.h"
+#include <multimedia/image_framework/image/pixelmap_native.h>
+
+Q_GLOBAL_STATIC(QOhPlatformAbilityCtrl, ctrl)
+
+QT_BEGIN_NAMESPACE
+
+static Napi::Value createWant(const QVariantMap &want)
+{
+ return QNapi::create(want);
+}
+
+static Napi::Object createAbilityResult(const QVariantMap &abilityResult)
+{
+ Napi::Object jsAbilityResult = Napi::Object::New(QtOh::uiEnv());
+ if (abilityResult.contains("resultCode"))
+ jsAbilityResult.Set("resultCode", abilityResult["resultCode"].toInt());
+
+ Napi::Object jswant = QNapi::create(abilityResult).ToObject();
+ if (jswant.Has("resultCode"))
+ jswant.Delete("resultCode");
+ jsAbilityResult.Set("want", jswant);
+ return jsAbilityResult;
+}
+
+static Napi::Value createStartOptions(const QVariantMap &startOptions)
+{
+ Napi::Object jsStartOptions = Napi::Object::New(QtOh::uiEnv());
+ if (startOptions.contains("windowMode"))
+ jsStartOptions.Set("windowMode", startOptions["windowMode"].toInt());
+
+ if (startOptions.contains("displayId"))
+ jsStartOptions.Set("displayId", startOptions["displayId"].toInt());
+
+ if (startOptions.contains("withAnimation"))
+ jsStartOptions.Set("withAnimation", startOptions["withAnimation"].toBool());
+
+ if (startOptions.contains("windowLeft"))
+ jsStartOptions.Set("windowLeft", startOptions["windowLeft"].toInt());
+
+ if (startOptions.contains("windowTop"))
+ jsStartOptions.Set("windowTop", startOptions["windowTop"].toInt());
+
+ if (startOptions.contains("windowWidth"))
+ jsStartOptions.Set("windowWidth", startOptions["windowWidth"].toInt());
+
+ if (startOptions.contains("windowHeight"))
+ jsStartOptions.Set("windowHeight", startOptions["windowHeight"].toInt());
+
+ if (startOptions.contains("processMode"))
+ jsStartOptions.Set("processMode", startOptions["processMode"].toInt());
+
+ if (startOptions.contains("startupVisibility"))
+ jsStartOptions.Set("startupVisibility", startOptions["startupVisibility"].toInt());
+
+ if (QtOh::apiVersion() >= 14) {
+ if (startOptions.contains("startWindowIcon")){
+ std::unique_ptr<OH_PixelmapNative, decltype(&::OH_PixelmapNative_Release)> nativePixel(QtOh::UdmfHelper::createNativePixelMapFromQImage(startOptions["startWindowIcon"].value<QPixmap>().toImage()),
+ ::OH_PixelmapNative_Release);
+ napi_value pixelMap { nullptr };
+ int result = ::OH_PixelmapNative_ConvertPixelmapNativeToNapi(QtOh::uiEnv(), nativePixel.get(), &pixelMap);
+ if (Image_ErrorCode::IMAGE_SUCCESS != result) {
+ qWarning("%s convert cursor pixmap failed. code: %d", Q_FUNC_INFO, result);
+ }else{
+ jsStartOptions.Set("startWindowIcon", Napi::Value::From(QtOh::uiEnv(), pixelMap));
+ }
+ }
+
+ if (startOptions.contains("startWindowBackgroundColor"))
+ jsStartOptions.Set("startWindowBackgroundColor", startOptions["startWindowBackgroundColor"].toString().toStdString());
+
+ if (startOptions.contains("supportWindowModes")){
+ QVariant storedList = startOptions["supportWindowModes"];
+ if (storedList.canConvert<QVariantList>()) {
+ QVariantList retrievedList = storedList.toList();
+ if(!retrievedList.isEmpty()){
+ Napi::Array ps = Napi::Array::New(QtOh::uiEnv(), retrievedList.size());
+ uint32_t index = 0;
+ for (auto &v : retrievedList) {
+ ps[index++] = v.toInt();
+ }
+ jsStartOptions.Set("supportWindowModes", ps);
+ }
+ }
+ }
+ }
+ return jsStartOptions;
+}
+
+void QOhPlatformAbilityCtrl::startAbility(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void (Napi::Value)> &resultHandler)
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return;
+ QtOh::runOnJsUIThreadNoWait([=]{
+ Napi::Function callback = Napi::Function::New(QtOh::uiEnv(), [=](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ LOGW("startAbility failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0)
+ LOGW("startAbility failed, code is %{public}d, message is %{public}s", code, error.Get("message").ToString().Utf8Value().c_str());
+ resultHandler(info[0]);
+ });
+ context->startAbility(createWant(want), createStartOptions(startOptions), callback);
+ });
+
+}
+
+void QOhPlatformAbilityCtrl::startAbilityForResult(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void (Napi::Value, Napi::Value)> &resultHandler)
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return;
+ QtOh::runOnJsUIThreadNoWait([=]{
+ Napi::Function callback = Napi::Function::New(QtOh::uiEnv(), [=](const Napi::CallbackInfo& info) {
+ if (info.Length() < 2) {
+ LOGW("startAbility failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0)
+ LOGW("startAbility failed, code is %{public}d, message is %{public}s", code, error.Get("message").ToString().Utf8Value().c_str());
+
+ resultHandler(info[0], info[1]);
+ });
+ context->startAbilityForResult(createWant(want), createStartOptions(startOptions), callback);
+ });
+}
+
+void QOhPlatformAbilityCtrl::terminateSelfWithResult(const QVariantMap &abilityResult)
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return;
+ QtOh::runOnJsUIThreadNoWait([=]{
+ context->terminateSelfWithResult(createAbilityResult(abilityResult));
+ });
+}
+
+QOhPlatformAbilityCtrl *QOhPlatformAbilityCtrl::instance()
+{
+ return ctrl();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QOHPLATFORMABILITYCTRL_H
+#define QOHPLATFORMABILITYCTRL_H
+
+#include <qpa/qplatformabilityctrl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformAbilityCtrl : public QPlatformAbilityCtrl
+{
+public:
+ static QOhPlatformAbilityCtrl *instance();
+
+ virtual void startAbility(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void (Napi::Value)> &resultHandler);
+ virtual void startAbilityForResult(const QVariantMap &want, const QVariantMap &startOptions,
+ const std::function<void(Napi::Value,Napi::Value)> &resultHandler);
+ virtual void terminateSelfWithResult(const QVariantMap &abilityResult);
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMABILITYCTRL_H
new file mode 100644
@@ -0,0 +1,438 @@
+#include <QPainter>
+#include <qpa/qplatformscreen.h>
+#include <qopenharmonydefines.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <native_window/external_window.h>
+#include <private/qimage_p.h>
+#include <private/qwindow_p.h>
+
+#include "qohmain.h"
+#include "qohplatformbackingstore.h"
+
+#include <cstring>
+#include <native_buffer/native_buffer.h>
+#include <native_window/buffer_handle.h>
+#include <QScopeGuard>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include "qohplatformwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+std::uint64_t getNativeWindowBufferQueueSize(::OHNativeWindow *nativeWindow)
+{
+ std::int32_t bufferQueueSize = 5;
+ auto getBufferQueueSizeResult = ::OH_NativeWindow_NativeWindowHandleOpt(
+ nativeWindow, ::NativeWindowOperation::GET_BUFFERQUEUE_SIZE, &bufferQueueSize);
+ if (Q_UNLIKELY(getBufferQueueSizeResult != 0)) {
+ qCWarning(lcQpaBackingStore) << "call OH_NativeWindow_NativeWindowHandleOpt failed." << getBufferQueueSizeResult;
+ }
+ return bufferQueueSize;
+}
+
+class BufferRegionHandler
+{
+public:
+ BufferRegionHandler(::OHNativeWindow *nativeWindow)
+ : m_bufferQueueSize(getNativeWindowBufferQueueSize(nativeWindow))
+ {
+ }
+
+ QRegion mergeRegionForBufferHandle(::BufferHandle *bufferHandle, QRegion region) const
+ {
+ const auto it = m_buffersToFlushSequenceIds.find(bufferHandle);
+ if (it == m_buffersToFlushSequenceIds.end()) {
+ return {};
+ }
+
+ std::uint64_t numberOfRegions = m_flushSequenceId - it->second;
+ if (numberOfRegions > m_bufferQueueSize) {
+ return {};
+ }
+
+ for (auto it = m_lastFlushedRegions.cend() - numberOfRegions;
+ it != m_lastFlushedRegions.cend(); ++it) {
+ region = region.united(*it);
+ }
+
+ return region;
+ }
+ void storeRegionForBufferHandle(::BufferHandle *bufferHandle, const QRegion ®ion)
+ {
+ m_buffersToFlushSequenceIds[bufferHandle] = m_flushSequenceId;
+
+ if (m_buffersToFlushSequenceIds.size() > m_bufferQueueSize) {
+ m_buffersToFlushSequenceIds.clear();
+ m_lastFlushedRegions.clear();
+ m_flushSequenceId = 0;
+ return;
+ }
+
+ m_lastFlushedRegions.push_back(region);
+ if (m_lastFlushedRegions.size() > m_bufferQueueSize) {
+ m_lastFlushedRegions.pop_front();
+ }
+
+ ++m_flushSequenceId;
+ }
+
+private:
+ const std::uint64_t m_bufferQueueSize;
+ std::uint64_t m_flushSequenceId { 0 };
+ std::unordered_map<::BufferHandle *, std::uint64_t> m_buffersToFlushSequenceIds;
+ std::deque<QRegion> m_lastFlushedRegions;
+};
+
+constexpr std::int32_t ohNativeWindowErrorCodeSuccess = 0;
+QOhPlatformBackingStore::QOhPlatformBackingStore(QWindow *window)
+ : QPlatformBackingStore(window)
+ , m_alphaNeedsFill(false)
+ , m_regionHandler(nullptr)
+{
+}
+
+QOhPlatformBackingStore::~QOhPlatformBackingStore()
+{
+
+}
+
+QPaintDevice *QOhPlatformBackingStore::paintDevice()
+{
+ return &m_image;
+}
+
+void QOhPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ QSize actualSize = size;
+ if (window()->handle()) {
+ QSize windowSize = window()->handle()->geometry().size();
+ actualSize = QSize(qMax(size.width(), windowSize.width()), qMax(size.height(), windowSize.height()));
+ }
+
+ if (m_image.isNull() || m_image.size() != actualSize) {
+ QImage::Format format = window()->screen()->handle()->format();
+
+ QImage oldImage = m_image;
+ if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha)
+ m_alphaNeedsFill = true;
+ else
+ format = qt_maybeAlphaVersionWithSameDepth(format);
+
+ QImage newImage(actualSize.width(), actualSize.height(), format);
+
+ if (!oldImage.isNull() && !staticContents.isEmpty()) {
+ QRegion staticRegion(staticContents);
+ staticRegion &= QRect(0, 0, oldImage.width(), oldImage.height());
+ staticRegion &= QRect(0, 0, newImage.width(), newImage.height());
+ QPainter painter(&newImage);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ for (const QRect &rect : staticRegion)
+ painter.drawImage(rect, oldImage, rect);
+ }
+
+ m_image = newImage;
+ }
+}
+
+Q_GUI_EXPORT void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QOhPlatformBackingStore::scroll(const QRegion &area, int dx, int dy)
+{
+ if (m_image.isNull())
+ return false;
+
+ const QPoint offset(dx, dy);
+ for (const QRect &rect : area)
+ qt_scrollRectInImage(m_image, rect, offset);
+
+ return true;
+}
+
+void QOhPlatformBackingStore::beginPaint(const QRegion ®ion)
+{
+ if (m_alphaNeedsFill) {
+ QPainter p(&m_image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QColor blank = Qt::transparent;
+ for (const QRect &r : region)
+ p.fillRect(r, blank);
+ }
+}
+
+QImage QOhPlatformBackingStore::toImage() const
+{
+ return m_image;
+}
+
+void QOhPlatformBackingStore::releaseVsync(QWindow *w)
+{
+#if OHOS_SDK_VERSION > 14
+ auto internalRelease = [](QWindow *w) {
+ if (m_vsyncs.contains(w)) {
+ auto vsync = m_vsyncs.take(w);
+ if (vsync) {
+ // 只做标记,在最后的callback中去做删除
+ vsync->setDeleted(true);
+ }
+ }
+ };
+ internalRelease(w);
+ auto children = w->findChildren<QWindow *>();
+ for (auto child : children) {
+ internalRelease(child);
+ }
+#endif
+}
+
+QImage::Format mapNativeBufferFormatToQImageFormatOrFail(std::int32_t format)
+{
+ QImage::Format result;
+
+ switch (format) {
+ case ::NATIVEBUFFER_PIXEL_FMT_RGB_888:
+ result = QImage::Format_RGB888;
+ break;
+ case ::NATIVEBUFFER_PIXEL_FMT_RGBA_8888:
+ result = QImage::Format_RGBA8888;
+ break;
+ default:
+ qFatal("%s: unsupported format %d", Q_FUNC_INFO, format);
+ }
+ return result;
+}
+bool tryMapBufferHandleMemory(::BufferHandle *bufferHandle, void *&outMemory)
+{
+ void *bufferMemory = ::mmap(
+ bufferHandle->virAddr, bufferHandle->size, PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0);
+
+ if (bufferMemory == MAP_FAILED) {
+ int mmapErrno = errno;
+ return false;
+ }
+
+ outMemory = bufferMemory;
+ return true;
+}
+
+void copyImage(const QImage &srcImage, QImage &dstImage)
+{
+ const int copyHeight = qMin(srcImage.height(), dstImage.height());
+
+ // 逐行拷贝重叠区域
+ for(int y = 0; y < copyHeight; ++y) {
+ const uchar* srcLine = srcImage.constScanLine(y);
+ uchar* dstLine = dstImage.scanLine(y);
+ std::memcpy(dstLine, srcLine, qMin(srcImage.bytesPerLine(), dstImage.bytesPerLine()));
+ }
+}
+
+void copyImage(const QImage &srcImage, QImage &dstImage, const QRegion ®ion)
+{
+ // 获取源图像和目标图像的有效区域
+ const QRect srcRect = QRect(0, 0, srcImage.width(), srcImage.height());
+ const QRect dstRect = QRect(0, 0, dstImage.width(), dstImage.height());
+
+ // 计算实际可拷贝的区域(考虑两个图像边界和指定区域)
+ QRegion validRegion = region;
+ validRegion &= srcRect; // 限制在源图像范围内
+ validRegion &= dstRect; // 限制在目标图像范围内
+
+ // 确保图像格式一致(至少深度/像素大小一致)
+ if(srcImage.depth() != dstImage.depth()) {
+ qCWarning(lcQpaBackingStore) << "Image depths differ, colors may be incorrect";
+ return;
+ }
+
+ const int bytesPerPixel = srcImage.depth() / 8;
+
+ // 遍历每个矩形区域
+ for(const QRect &rect : validRegion) {
+ // 计算每行的起始位置和拷贝字节数
+ const int xSrc = rect.x() * bytesPerPixel;
+ const int xDst = rect.x() * bytesPerPixel;
+ const int copyBytes = rect.width() * bytesPerPixel;
+
+ // 确保不会越界(二次检查)
+ const int srcMaxOffset = srcImage.bytesPerLine() - xSrc;
+ const int dstMaxOffset = dstImage.bytesPerLine() - xDst;
+ const int actualCopyBytes = qMin(copyBytes, qMin(srcMaxOffset, dstMaxOffset));
+
+ if(actualCopyBytes <= 0) continue;
+
+ // 逐行拷贝
+ for(int row = 0; row < rect.height(); ++row) {
+ const int y = rect.y() + row;
+
+ // 再次检查行范围
+ if(y >= srcImage.height() || y >= dstImage.height()) break;
+
+ const uchar* srcRow = srcImage.constScanLine(y) + xSrc;
+ uchar* dstRow = dstImage.scanLine(y) + xDst;
+
+ std::memcpy(dstRow, srcRow, actualCopyBytes);
+ }
+ }
+}
+
+std::vector<::Region::Rect> makeOhosRegionRectsForFlush(
+ const QRegion ®ion, const QPoint &rootwindowOffset, const QSize &dstImageSize)
+{
+ std::vector<::Region::Rect> rects;
+ if (!region.isEmpty()){
+ std::transform(
+ region.begin(), region.end(), std::back_inserter(rects),
+ [&](const auto &qrect){
+ return ::Region::Rect{
+ .x = qrect.x() + rootwindowOffset.x(),
+ .y = (dstImageSize.height() - qrect.y()) + rootwindowOffset.y() - qrect.height(),
+ .w = static_cast<std::uint32_t>(qrect.width()),
+ .h = static_cast<std::uint32_t>(qrect.height()),
+ };
+ });
+ }
+ return rects;
+}
+
+
+void QOhPlatformBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset)
+{
+ auto platformWindow = window->handle();
+ if (platformWindow == nullptr) {
+ qCWarning(lcQpaBackingStore) << window << "native window has destroyed";
+ return;
+ }
+ if (!platformWindow->isExposed()) {
+ qCWarning(lcQpaBackingStore) << window << "window not exposed";
+ return;
+ }
+ WId id = window->winId();
+ WindowNode *node = reinterpret_cast<WindowNode *>(id);
+
+ if (node == nullptr) {
+ qCWarning(lcQpaBackingStore) << "get window node failed.";
+ return;
+ }
+
+ OHNativeWindow *nativeWindow = reinterpret_cast<OHNativeWindow *>(node->window);
+ if (nativeWindow == nullptr) {
+ qCWarning(lcQpaBackingStore) << Q_FUNC_INFO << "get xcomponent node window failed, window" << window;
+ return;
+ }
+
+ if (m_regionHandler == nullptr) {
+ m_regionHandler = std::make_unique<BufferRegionHandler>(nativeWindow);
+ }
+
+#if OHOS_SDK_VERSION > 14
+ if (!m_vsyncs.contains(window)) {
+ m_vsyncs.insert(window, new QOhNativeVSync(window, this));
+ }
+
+ auto vsync = m_vsyncs.value(window);
+ if (vsync) {
+ if (!vsync->isReady()) {
+ // qCWarning(lcQpaBackingStore) << "OHOS buffer not ready, wait for its ready.";
+ vsync->saveFrame(window, region, offset);
+ return;
+ } else if (vsync->isPendingFlush()) {
+ vsync->flush();
+ vsync->saveFrame(window, region, offset);
+ return;
+ } else {
+ vsync->clearFlag();
+ }
+ }
+#endif
+ int fenceFd = -1;
+ OHNativeWindowBuffer *buffer = nullptr;
+ int ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd);
+ if (ret != 0 || nullptr == buffer) {
+ qCWarning(lcQpaBackingStore) << "call OH_NativeWindow_NativeWindowRequestBuffer failed.";
+ return;
+ }
+ bool isRootWindow = window == this->window();
+ const QRect windowRect = platformWindow->geometry();
+ QPoint copyPoint(qMax(0, offset.x()), qMax(0, offset.y()));
+ QRect copyRect = QRect(copyPoint, windowRect.size());
+ QImage srcImage = isRootWindow
+ ? m_image
+ : m_image.copy(copyRect.intersected(m_image.rect()));
+
+ auto nativeWindowAbortBufferGuard = qScopeGuard([nativeWindow, buffer](){
+ auto abortBufferEc = ::OH_NativeWindow_NativeWindowAbortBuffer(nativeWindow, buffer);
+ if (abortBufferEc != ohNativeWindowErrorCodeSuccess) {
+ return;
+ }
+ });
+
+ BufferHandle *bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
+ if (bufferHandle == nullptr) {
+ qCWarning(lcQpaBackingStore) << "call OH_NativeWindow_GetBufferHandleFromNative failed.";
+ return;
+ }
+
+#if OHOS_SDK_VERSION > 14
+ // 如果QWidget主动设置大小之后绘制时,
+ // 由于异步设置系统窗口大小还未正确改变成设置的大小,导致绘制不完整。保存重新绘制
+ auto geometry = platformWindow->geometry();
+ if (bufferHandle->width != geometry.width() || bufferHandle->height != geometry.height())
+ vsync->saveFrame(window, region, offset);
+#endif
+ int width = qMin(bufferHandle->width, m_image.width());
+ int height = qMin(bufferHandle->height, m_image.height());
+
+ int retCode = -1;
+ uint32_t timeout = 3000;
+ if (fenceFd != -1) {
+ struct pollfd pollfds = {fenceFd, POLLIN, 0};
+ do {
+ retCode = poll(&pollfds, 1, timeout);
+ } while (retCode == -1 && (errno == EINTR || errno == EAGAIN));
+ close(fenceFd); /* 防止fd泄漏 */
+ }
+
+ QImage::Format dstImageFormat = mapNativeBufferFormatToQImageFormatOrFail(bufferHandle->format);
+ QSize dstImageSize{width, height};
+ uchar *mappedAddress =
+ static_cast<uchar*>(mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0));
+
+ if (mappedAddress == MAP_FAILED) {
+ qCWarning(lcQpaBackingStore) << "mmap failed";
+ return;
+ }
+
+ if (offset.y() < 0) {
+ int offsetY = abs(offset.y());
+ if (offsetY + dstImageSize.height() > windowRect.height())
+ offsetY = windowRect.height() - dstImageSize.height();
+ mappedAddress += bufferHandle->stride * offsetY;
+ }
+ QImage dstImage(
+ mappedAddress,
+ dstImageSize.width(), dstImageSize.height(),
+ bufferHandle->stride,
+ dstImageFormat);
+
+ const auto& mergedRegionOpt = m_regionHandler->mergeRegionForBufferHandle(bufferHandle, region);
+
+ std::vector<::Region::Rect> rects;
+ if (mergedRegionOpt.isEmpty() || mergedRegionOpt.isNull()) {
+ copyImage(srcImage, dstImage);
+ rects = makeOhosRegionRectsForFlush(QRegion(), isRootWindow ? QPoint{} : offset, dstImage.size());
+ } else {
+ copyImage(srcImage, dstImage, mergedRegionOpt);
+ rects = makeOhosRegionRectsForFlush(mergedRegionOpt, isRootWindow ? QPoint{} : offset, dstImage.size());
+ }
+
+ OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, -1,
+ ::Region{
+ .rects = rects.empty() ? nullptr : rects.data(),
+ .rectNumber = static_cast<std::int32_t>(rects.size()),
+ });
+ nativeWindowAbortBufferGuard.dismiss();
+ munmap(mappedAddress, bufferHandle->size);
+ m_regionHandler->storeRegionForBufferHandle(bufferHandle, region);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,42 @@
+#ifndef QOHPLATFORMBACKINGSTORE_H
+#define QOHPLATFORMBACKINGSTORE_H
+
+#include <qpa/qplatformbackingstore.h>
+#include "qohnativevsync.h"
+
+#include <deque>
+#include <native_window/buffer_handle.h>
+#include <native_window/external_window.h>
+#include <unordered_map>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class BufferRegionHandler;
+class QOhPlatformBackingStore : public QPlatformBackingStore
+{
+public:
+ explicit QOhPlatformBackingStore(QWindow *window);
+ ~QOhPlatformBackingStore();
+ QPaintDevice *paintDevice() override;
+
+ void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
+ bool scroll(const QRegion &area, int dx, int dy) override;
+ void beginPaint(const QRegion &) override;
+ QImage toImage() const override;
+
+ static void releaseVsync(QWindow *w);
+
+private:
+ QImage m_image;
+ bool m_alphaNeedsFill;
+#if OHOS_SDK_VERSION > 14
+ static inline QHash<QWindow *, QOhNativeVSync *> m_vsyncs = {};
+#endif
+
+ std::unique_ptr<BufferRegionHandler> m_regionHandler;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMBACKINGSTORE_H
new file mode 100644
@@ -0,0 +1,257 @@
+#include <QSet>
+#include <QUrl>
+#include <QImage>
+#include <QBuffer>
+#include <QTextDocument>
+#include <QLoggingCategory>
+#include <QtCore/qopenharmonydefines.h>
+
+#include <typeinfo>
+#include <database/udmf/udmf_meta.h>
+#include <database/udmf/udmf_err_code.h>
+#include <database/pasteboard/oh_pasteboard.h>
+#include <database/pasteboard/oh_pasteboard_err_code.h>
+
+#include "qohauxiliary.h"
+#include "qohplatformclipboard.h"
+#include "qpa/qplatformaccessctrl.h"
+#include "qpa/qplatformintegration.h"
+#include "private/qguiapplication_p.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#define qClipBoard QOhPlatformClipboard::instance()
+
+Q_LOGGING_CATEGORY(clipboard, "qt.ohos.clipboard")
+
+/* TODO 系统提供的剪切板相关处理不完善
+ * 无法进行延迟数据读取等操作
+ */
+class QOhPasteDataObject
+{
+public:
+ explicit QOhPasteDataObject(QMimeData *data) : m_data(data){}
+ ~QOhPasteDataObject() = default;
+
+ void releaseQt() { m_data = nullptr; }
+ QMimeData *mimeData() { return m_data.data(); }
+
+private:
+ QPointer<QMimeData> m_data;
+};
+
+/*!
+ * \brief 剪贴板数据变更观察者对象销毁时的通知回调函数
+ */
+void QOhPlatformClipboard::pasteboardFinalizeImpl(void *context)
+{
+ Q_UNUSED(context);
+ LOGW("pasteboard finalize impl");
+}
+
+/*!
+ * \brief 贴板数据内容变更时的通知回调函数
+ */
+void QOhPlatformClipboard::pasteboardNofityImpl(void *context, ::Pasteboard_NotifyType type)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(context);
+
+ QtOh::runOnQtMainThread([]{
+ if (qClipBoard) {
+ qClipBoard->emitChanged(QClipboard::Clipboard);
+ }
+ });
+ LOGW("pasteboard notify impl");
+}
+
+#if OHOS_SDK_VERSION >= 15
+void QOhPlatformClipboard::reportProgress()
+{
+ int status = ::PASTEBOARD_ErrCode::ERR_OK;
+ ::OH_UdmfData *data = ::OH_Pasteboard_GetDataWithProgress(m_pasteboard.get(), m_params.get(), &status);
+ if (::PASTEBOARD_ErrCode::ERR_OK != status) {
+ qCWarning(clipboard, "%s get data with progress failed: %d", Q_FUNC_INFO, status);
+ }
+ ::OH_UdmfData_Destroy(data);
+}
+
+QClipboard::ProgressMode QOhPlatformClipboard::progressMode() const
+{
+ return m_progressMode;
+}
+
+void QOhPlatformClipboard::setProgressMode(QClipboard::ProgressMode mode)
+{
+ /* 进度回调---用于非默认进度显示 表示进度数据变化的订阅函数
+ * 当选择不使用系统默认进度显示时,可设置该项获取粘贴过程的进度
+ */
+ if (QClipboard::ProgressMode::Default == mode) {
+ ::OH_Pasteboard_GetDataParams_SetProgressIndicator(m_params.get(), ::Pasteboard_ProgressIndicator::PASTEBOARD_DEFAULT);
+ ::OH_Pasteboard_GetDataParams_SetProgressListener(m_params.get(), nullptr);
+ } else if (QClipboard::ProgressMode::None == mode) {
+ ::OH_Pasteboard_GetDataParams_SetProgressIndicator(m_params.get(), ::Pasteboard_ProgressIndicator::PASTEBOARD_NONE);
+ ::OH_Pasteboard_GetDataParams_SetProgressListener(m_params.get(), &QOhPlatformClipboard::pasteboardProgressListener);
+ }
+ m_progressMode = mode;
+}
+
+/*!
+ * \brief 非默认进度条显示-回调函数
+ * \param progressInfo 定义进度上报的数据结构
+ * 且仅当进度指示选项Pasteboard_ProgressIndicator设置为NONE时才会上报此信息
+ */
+void QOhPlatformClipboard::pasteboardProgressListener(::Pasteboard_ProgressInfo *progressInfo)
+{
+ /* 通过Pasteboard_ProgressInfo获取粘贴进度
+ * 且仅当进度指示选项Pasteboard_ProgressIndicator设置
+ * 为PASTEBOARD_NONE时,才可以获取到此信息
+ */
+ int percentage = ::OH_Pasteboard_ProgressInfo_GetProgress(progressInfo);
+ if (qClipBoard) {
+ QMetaObject::invokeMethod(QGuiApplication::clipboard(), "progressValueChanged", Q_ARG(int, percentage));
+ }
+ LOGI("%{public}s, percentage: %{public}d", Q_FUNC_INFO, percentage);
+}
+#endif
+
+template<typename T, typename Deleter>
+QSharedPointer<T> newSharedOrFail(T *ptr, Deleter &&deleter)
+{
+ if (ptr == nullptr) {
+ qFatal("%s: got null pointer with deleter of type '%s'",
+ Q_FUNC_INFO, typeid(deleter).name());
+ }
+
+ return QSharedPointer<T>(ptr, deleter);
+}
+
+QOhPlatformClipboard::QOhPlatformClipboard()
+ : m_mimeData(new QMimeData())
+ , m_pasteboard(nullptr)
+ , m_observer(nullptr)
+#if OHOS_SDK_VERSION >= 15
+ , m_progressMode(QClipboard::ProgressMode::None)
+ , m_params(nullptr)
+#endif
+{
+ m_pasteboard = newSharedOrFail(::OH_Pasteboard_Create(), ::OH_Pasteboard_Destroy); /* 创建剪贴板实例 */
+ m_observer = newSharedOrFail(::OH_PasteboardObserver_Create(), ::OH_PasteboardObserver_Destroy); /* 创建剪贴板数据变更观察者实例 */
+#if OHOS_SDK_VERSION >= 15
+ m_params = newSharedOrFail(::OH_Pasteboard_GetDataParams_Create(), ::OH_Pasteboard_GetDataParams_Destroy);
+ /* 向剪贴板设置进度条指示选项,采用系统默认进度显示
+ * PASTEBOARD_NONE 不采用系统默认进度显示
+ * PASTEBOARD_DEFAULT 采用系统默认进度显示。
+ */
+ ::OH_Pasteboard_GetDataParams_SetProgressIndicator(m_params.get(), ::Pasteboard_ProgressIndicator::PASTEBOARD_NONE);
+
+ /* 进度回调---用于非默认进度显示 表示进度数据变化的订阅函数
+ * 当选择不使用系统默认进度显示时,可设置该项获取粘贴过程的进度
+ */
+ ::OH_Pasteboard_GetDataParams_SetProgressListener(m_params.get(), &QOhPlatformClipboard::pasteboardProgressListener);
+#endif
+ /* 将两个回调函数设置到观察者实例 */
+ ::OH_PasteboardObserver_SetData(m_observer.get(), reinterpret_cast<void*>(this),
+ &QOhPlatformClipboard::pasteboardNofityImpl,
+ &QOhPlatformClipboard::pasteboardFinalizeImpl);
+
+ /* 设置对剪贴板本地设备数据变化的订阅 */
+ ::OH_Pasteboard_Subscribe(m_pasteboard.get(), NOTIFY_LOCAL_DATA_CHANGE, m_observer.get());
+ m_self = this;
+}
+
+QOhPlatformClipboard::~QOhPlatformClipboard()
+{
+#if OHOS_SDK_VERSION >= 15
+ ::OH_Pasteboard_ProgressCancel(m_params.get());
+#endif
+ OH_Pasteboard_Unsubscribe(m_pasteboard.get(), NOTIFY_LOCAL_DATA_CHANGE, m_observer.get());
+ /* TODO 应用在harmony平台对剪贴板是否存在所有权?,需要责清理系统剪贴板数据吗? */
+ //OH_Pasteboard_ClearData(m_pasteboard.get());
+
+ if (m_mimeData) {
+ m_mimeData->deleteLater();
+ m_mimeData = nullptr;
+ }
+
+ m_self = nullptr;
+}
+
+QOhPlatformClipboard *QOhPlatformClipboard::instance()
+{
+ return m_self;
+}
+
+QMimeData *QOhPlatformClipboard::mimeData(QClipboard::Mode mode)
+{
+ Q_UNUSED(mode);
+ if (!supportsMode(mode))
+ return nullptr;
+
+ /* 检查剪贴板中是否有数据 */
+ if (!OH_Pasteboard_HasData(m_pasteboard.get())) {
+ qCWarning(clipboard) << Q_FUNC_INFO << "the clipboard has no data.";
+ return m_mimeData.data();
+ }
+ int status = ::PASTEBOARD_ErrCode::ERR_OK;
+ ::OH_UdmfData *data = ::OH_Pasteboard_GetData(m_pasteboard.get(), &status);
+ if (::PASTEBOARD_ErrCode::ERR_OK != status) {
+ ::OH_UdmfData_Destroy(data);
+ qCWarning(clipboard, "%s get clipboard data failed: %d", Q_FUNC_INFO, status);
+ return m_mimeData.data();
+ }
+
+ QtOh::UdmfHelper::acquireDatasFromUdmfToMime(data, m_mimeData);
+ return m_mimeData.data();
+}
+
+void QOhPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
+{
+ if (!supportsMode(mode)) {
+ qCDebug(clipboard) << "the platform does not support clipboard mode:" << mode;
+ return;
+ }
+
+ const bool newData = !m_mimeData || m_mimeData != data;
+ if (newData) {
+ if (data) {
+ m_mimeData ? [this](){ delete m_mimeData; m_mimeData = nullptr; }() : void();
+#if OHOS_SDK_VERSION >= 15
+ ::OH_Pasteboard_ProgressCancel(m_params.get());
+#endif
+ m_mimeData = data;
+ } else {
+ m_mimeData = new QMimeData();
+ }
+ }
+
+ OH_UdmfData *t = QtOh::UdmfHelper::makeUdmfDataFromMime(m_mimeData);
+ int code = ::OH_Pasteboard_SetData(m_pasteboard.get(), t);
+ if (PASTEBOARD_ErrCode::ERR_OK != code) {
+ OH_UdmfData_Destroy(t);
+ qCWarning(clipboard, "%s set pasteboard data failed, code: %d", Q_FUNC_INFO, code);
+ }
+}
+
+bool QOhPlatformClipboard::supportsMode(QClipboard::Mode mode) const
+{
+ return QClipboard::Clipboard == mode;
+}
+
+void QOhPlatformClipboard::releaseData()
+{
+ if (m_data) {
+ delete m_data->mimeData();
+ m_data->releaseQt();
+
+ delete m_data;
+ m_data = nullptr;
+ }
+}
+
+bool QOhPlatformClipboard::ownsClipboard() const
+{
+ return m_data;
+}
+
+#endif // QT_NO_CLIPBOARD
new file mode 100644
@@ -0,0 +1,54 @@
+#ifndef QOHPLATFORMCLIPBOARD_H
+#define QOHPLATFORMCLIPBOARD_H
+
+#include <QPointer>
+#include <QMimeData>
+#include <qpa/qplatformclipboard.h>
+
+#include <database/pasteboard/oh_pasteboard.h>
+
+#ifndef QT_NO_CLIPBOARD
+
+#if OHOS_SDK_VERSION >= 15
+struct Pasteboard_GetDataParams;
+#endif
+
+class QOhPasteDataObject;
+
+class QOhPlatformClipboard: public QPlatformClipboard
+{
+public:
+ QOhPlatformClipboard();
+ ~QOhPlatformClipboard();
+ static QOhPlatformClipboard *instance();
+
+ QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ bool supportsMode(QClipboard::Mode mode) const override;
+
+private:
+ void releaseData();
+ bool ownsClipboard() const;
+ static void pasteboardFinalizeImpl(void *context);
+ static void pasteboardNofityImpl(void *context, ::Pasteboard_NotifyType type);
+#if OHOS_SDK_VERSION >= 15
+ void reportProgress() override;
+ QClipboard::ProgressMode progressMode() const override;
+ void setProgressMode(QClipboard::ProgressMode mode) override;
+ static void pasteboardProgressListener(::Pasteboard_ProgressInfo* progressInfo);
+#endif
+private:
+ QPointer<QMimeData> m_mimeData;
+ static inline QOhPlatformClipboard *m_self = nullptr;
+ QOhPasteDataObject *m_data = nullptr;
+ QSharedPointer<OH_Pasteboard> m_pasteboard;
+ QSharedPointer<OH_PasteboardObserver> m_observer;
+#if OHOS_SDK_VERSION >= 15
+ QClipboard::ProgressMode m_progressMode;
+ QSharedPointer<Pasteboard_GetDataParams> m_params;
+#endif
+};
+
+#endif // QT_NO_CLIPBOARD
+
+#endif // QOHPLATFORMCLIPBOARD_H
new file mode 100644
@@ -0,0 +1,241 @@
+#include "qohplatformcursor.h"
+#include "jsclass/qjscursor.h"
+#include "qohplatformwindow.h"
+#include "qohwindowcontext.h"
+#include <QtCore/qopenharmonydefines.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QCursor>
+#include <QPixmap>
+#include <QImage>
+#include <QByteArray>
+#include <QBuffer>
+#include <QIODevice>
+#include <QBitmap>
+
+QT_BEGIN_NAMESPACE
+
+PixmapCursorCacheKey::PixmapCursorCacheKey(const QCursor &c)
+ : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0)
+{
+ if (!bitmapCacheKey) {
+ Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull());
+ Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull());
+ bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey();
+ maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey();
+ }
+}
+
+QOhPlatformCursor::QOhPlatformCursor(const QPlatformScreen *screen)
+ : m_screen(screen)
+{
+ if (!m_jsCursor)
+ m_jsCursor.reset(new QJsCursor);
+ if (!m_defaultCursor)
+ m_defaultCursor.reset(new QCursorInfo{QJsCursor::DEFAULT});
+}
+
+void QOhPlatformCursor::changeCursor(QCursor *windowCursor, QWindow *window)
+{
+ QOhPlatformWindow *platformWindow = QOhWindowContext::get(window);
+ if (!platformWindow) // Desktop/Foreign window.
+ return;
+
+ if (!windowCursor) {
+ platformWindow->setCursor(nullptr);
+ return;
+ };
+
+ QCursorInfo *wcursor = cursorInfo(*windowCursor);
+ if (wcursor != nullptr) {
+ platformWindow->setCursor(wcursor);
+ }
+}
+
+void QOhPlatformCursor::setOverrideCursor(const QCursor &cursor)
+{
+ m_overrideCursor = cursorInfo(cursor);
+ if (m_overrideCursor != nullptr && m_windowId != -1) {
+ m_jsCursor->setCursor(m_windowId, m_overrideCursor);
+ }
+}
+
+void QOhPlatformCursor::clearOverrideCursor()
+{
+ if (m_overrideCursor != nullptr) {
+ m_overrideCursor = nullptr;
+ if (m_currentCursor != nullptr && m_windowId != -1) {
+ m_jsCursor->setCursor(m_windowId, m_currentCursor);
+ }
+ }
+}
+
+QPoint QOhPlatformCursor::pos() const
+{
+ return m_pos;
+}
+
+void QOhPlatformCursor::setPos(const QPoint &pos)
+{
+ m_pos = pos;
+}
+
+QSize QOhPlatformCursor::size() const
+{
+ return QPlatformCursor::size();
+}
+
+void QOhPlatformCursor::setWindowOverrideCursor(int windowId, QCursorInfo *info)
+{
+ if (info != nullptr)
+ m_jsCursor->setCursor(windowId, info);
+}
+
+void QOhPlatformCursor::setCursor(int windowId, QCursorInfo *info)
+{
+ m_windowId = windowId;
+ m_currentCursor = info;
+ m_jsCursor->setCursor(windowId, info);
+}
+
+void QOhPlatformCursor::setDefaultCursor(int windowId)
+{
+ m_jsCursor->setCursor(windowId, m_defaultCursor.get());
+}
+
+void QOhPlatformCursor::enforceOverrideCursor(int windowId)
+{
+ if (hasOverrideCursor()) {
+ m_windowId = windowId;
+ m_jsCursor->setCursor(windowId, m_overrideCursor);
+ }
+}
+
+QCursorInfo *QOhPlatformCursor::cursorInfo(const QCursor &cursor)
+{
+ return (cursor.shape() == Qt::BitmapCursor || cursor.shape() == Qt::CustomCursor)
+ ? pixmapWindowCursor(cursor)
+ : standardWindowCursor(cursor.shape());
+}
+
+QCursorInfo *QOhPlatformCursor::pixmapWindowCursor(const QCursor &c)
+{
+ const PixmapCursorCacheKey cacheKey(c);
+ PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey);
+ if (it == m_pixmapCursorCache.end()) {
+ if (m_pixmapCursorCache.size() > 50) {
+ for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) {
+ if (it.value() != m_currentCursor) {
+ delete it.value();
+ it = m_pixmapCursorCache.erase(it);
+ }
+ else
+ ++it;
+ }
+ }
+ const qreal scaleFactor = QHighDpiScaling::factor(m_screen);
+ const QPixmap pixmap = c.pixmap();
+ QCursorInfo *info = createPixmapCursor(pixmap, c.hotSpot(), scaleFactor);
+ it = m_pixmapCursorCache.insert(cacheKey, info);
+ }
+ return it.value();
+}
+
+QCursorInfo *QOhPlatformCursor::createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor)
+{
+ const qreal pixmapScaleFactor = scaleFactor / pixmap.devicePixelRatioF();
+ if (!qFuzzyCompare(pixmapScaleFactor, 1)) {
+ pixmap = pixmap.scaled((pixmapScaleFactor * QSizeF(pixmap.size())).toSize(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ QCursorInfo *cur = new QCursorInfo{ QJsCursor::CUSTOM, int(hotSpot.x() * scaleFactor),
+ int(hotSpot.y() * scaleFactor), pixmap };
+ return cur;
+}
+
+QCursorInfo *QOhPlatformCursor::standardWindowCursor(Qt::CursorShape s)
+{
+ StandardCursorCache::Iterator it = m_standardCursorCache.find(s);
+ if (it == m_standardCursorCache.end()) {
+ QJsCursor::PointerStyle pointerStyle = QJsCursor::DEFAULT;
+ switch (s) {
+ case Qt::ArrowCursor:
+ pointerStyle = QJsCursor::DEFAULT;
+ break;
+ case Qt::UpArrowCursor:
+ pointerStyle = QJsCursor::NORTH;
+ break;
+ case Qt::CrossCursor:
+ pointerStyle = QJsCursor::CROSS;
+ break;
+ case Qt::WaitCursor:
+ pointerStyle = QJsCursor::LOADING;
+ break;
+ case Qt::IBeamCursor:
+ pointerStyle = QJsCursor::TEXT_CURSOR;
+ break;
+ case Qt::SizeVerCursor:
+ pointerStyle = QJsCursor::NORTH_SOUTH;
+ break;
+ case Qt::SizeHorCursor:
+ pointerStyle = QJsCursor::WEST_EAST;
+ break;
+ case Qt::SizeBDiagCursor:
+ pointerStyle = QJsCursor::NORTH_EAST_SOUTH_WEST;
+ break;
+ case Qt::SizeFDiagCursor:
+ pointerStyle = QJsCursor::NORTH_WEST_SOUTH_EAST;
+ break;
+ case Qt::SizeAllCursor:
+ pointerStyle = QJsCursor::MOVE;
+ break;
+ case Qt::SplitVCursor:
+ pointerStyle = QJsCursor::RESIZE_UP_DOWN;
+ break;
+ case Qt::SplitHCursor:
+ pointerStyle = QJsCursor::RESIZE_LEFT_RIGHT;
+ break;
+ case Qt::PointingHandCursor:
+ pointerStyle = QJsCursor::HAND_POINTING;
+ break;
+ case Qt::ForbiddenCursor:
+ pointerStyle = QJsCursor::CURSOR_FORBID;
+ break;
+ case Qt::WhatsThisCursor:
+ pointerStyle = QJsCursor::HELP;
+ break;
+ //todo:鸿蒙没有对应的样式,暂时用"加载中"样式
+ case Qt::BusyCursor:
+ pointerStyle = QJsCursor::LOADING;
+ break;
+ case Qt::OpenHandCursor:
+ pointerStyle = QJsCursor::HAND_OPEN;
+ break;
+ case Qt::ClosedHandCursor:
+ pointerStyle = QJsCursor::HAND_GRABBING;
+ break;
+ //todo:鸿蒙没有对应的样式,暂时用"默认"样式
+ case Qt::DragCopyCursor:
+ pointerStyle = QJsCursor::DEFAULT;
+ break;
+ //todo:鸿蒙没有对应样式,暂时用"默认"样式
+ case Qt::DragMoveCursor:
+ pointerStyle = QJsCursor::DEFAULT;
+ break;
+ //todo:鸿蒙没有对应样式,暂时用"默认"样式
+ case Qt::DragLinkCursor:
+ pointerStyle = QJsCursor::DEFAULT;
+ break;
+ case Qt::BlankCursor:
+ pointerStyle = QJsCursor::HIDE;
+ break;
+ default:
+ pointerStyle = QJsCursor::DEFAULT;
+ break;
+ }
+ it = m_standardCursorCache.insert(s, new QCursorInfo{pointerStyle});
+ }
+ return it != m_standardCursorCache.end() ? it.value() : m_defaultCursor.get();
+}
+
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,76 @@
+
+#ifndef QOHPLATFORMCURSOR_H
+#define QOHPLATFORMCURSOR_H
+
+#include <qpa/qplatformcursor.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qhash.h>
+#include <QSize>
+#include <QPoint>
+#include "qjscursor.h"
+
+QT_BEGIN_NAMESPACE
+struct QCursorInfo;
+
+struct PixmapCursorCacheKey
+{
+ explicit PixmapCursorCacheKey(const QCursor &c);
+
+ qint64 bitmapCacheKey;
+ qint64 maskCacheKey;
+};
+
+inline bool operator==(const PixmapCursorCacheKey &k1, const PixmapCursorCacheKey &k2)
+{
+ return k1.bitmapCacheKey == k2.bitmapCacheKey && k1.maskCacheKey == k2.maskCacheKey;
+}
+
+inline uint qHash(const PixmapCursorCacheKey &k, uint seed) noexcept
+{
+ return (uint(k.bitmapCacheKey) + uint(k.maskCacheKey)) ^ seed;
+}
+
+class QOhPlatformCursor : public QPlatformCursor
+{
+public:
+ explicit QOhPlatformCursor(const QPlatformScreen *screen);
+
+ void changeCursor(QCursor * widgetCursor, QWindow * widget) override;
+ void setOverrideCursor(const QCursor &cursor) override;
+ void clearOverrideCursor() override;
+
+ QPoint pos() const override;
+ void setPos(const QPoint &pos) override;
+ QSize size() const override;
+
+ static void setWindowOverrideCursor(int windowId, QCursorInfo *info);
+ static void setCursor(int windowId, QCursorInfo *info);
+ static void setDefaultCursor(int windowId);
+ static void enforceOverrideCursor(int windowId);
+ static bool hasOverrideCursor() { return m_overrideCursor != nullptr; }
+private:
+ inline QCursorInfo *cursorInfo(const QCursor &cursor);
+ QCursorInfo *standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor);
+ QCursorInfo *pixmapWindowCursor(const QCursor &c);
+ QCursorInfo *createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor);
+
+
+ typedef QHash<PixmapCursorCacheKey, QCursorInfo*> PixmapCursorCache;
+ typedef QHash<Qt::CursorShape, QCursorInfo*> StandardCursorCache;
+
+ const QPlatformScreen *const m_screen;
+ QPoint m_pos;
+ PixmapCursorCache m_pixmapCursorCache;
+ StandardCursorCache m_standardCursorCache;
+
+ static inline std::unique_ptr<QJsCursor> m_jsCursor = nullptr;
+ static inline QCursorInfo *m_overriddenCursor = nullptr;
+ static inline QCursorInfo *m_overrideCursor = nullptr;
+ static inline QCursorInfo *m_currentCursor = nullptr;
+ static inline std::unique_ptr<QCursorInfo> m_defaultCursor = nullptr;
+ static inline int m_windowId = -1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMCURSOR_H
new file mode 100644
@@ -0,0 +1,738 @@
+#include <QDebug>
+#include <QTimer>
+#include <QWindow>
+#include <QFileInfo>
+#include <QJsModule>
+#include <QScopeGuard>
+#include <QStringList>
+#include <QTextDocument>
+#include <QGuiApplication>
+#include <QEventLoopLocker>
+#include <qpa/qplatformtheme.h>
+#include <private/qguiapplication_p.h>
+#include <QtCore/qopenharmonydefines.h>
+#include <qpa/qplatformnativeinterface.h>
+
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include "qjscontext.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatformdialoghelpers.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOhNativeDialogBase : public QObject
+{
+ Q_OBJECT
+public:
+ QOhNativeDialogBase() {}
+ virtual ~QOhNativeDialogBase() {}
+ void exec(QWindow *parent) { doExec(parent); m_executed = true; }
+ bool executed() const { return m_executed; }
+signals:
+ void accepted();
+ void rejected();
+protected:
+ virtual void doExec(QWindow *parent) = 0;
+private:
+ bool m_executed = false;
+};
+
+class QOhPlatformMessageDialogHelper: public QPlatformMessageDialogHelper
+{
+ Q_OBJECT
+public:
+ QOhPlatformMessageDialogHelper();
+ ~QOhPlatformMessageDialogHelper();
+ void exec() override;
+ bool show(Qt::WindowFlags windowFlags,
+ Qt::WindowModality windowModality,
+ QWindow *parent) override;
+ void hide() override;
+
+public slots:
+ void setDialogResult(int buttonID);
+
+private:
+ void addButtons(QSharedPointer<QMessageDialogOptions> opt, ButtonRole role);
+
+private:
+ int m_buttonId;
+ bool m_shown;
+ QStringList m_buttons;
+ QList<int> m_buttonIds;
+};
+
+QOhPlatformMessageDialogHelper::QOhPlatformMessageDialogHelper()
+ : m_buttonId(-1)
+ , m_shown(false)
+{
+
+}
+
+QOhPlatformMessageDialogHelper::~QOhPlatformMessageDialogHelper()
+{
+
+}
+
+void QOhPlatformMessageDialogHelper::exec()
+{
+ if (!m_shown)
+ show(Qt::Dialog, Qt::ApplicationModal, 0);
+}
+
+static QString htmlText(QString text)
+{
+ if (Qt::mightBeRichText(text))
+ return text;
+ text.remove(QLatin1Char('\r'));
+ return text.toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("<br />"));
+}
+
+bool QOhPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags
+ , Qt::WindowModality windowModality
+ , QWindow *parent)
+{
+ if (m_shown)
+ return true;
+ Q_UNUSED(windowFlags)
+ Q_UNUSED(windowModality)
+ Q_UNUSED(parent)
+
+ QSharedPointer<QMessageDialogOptions> opt = options();
+ if (!opt.data())
+ return false;
+
+ m_buttons.clear();
+ m_buttonIds.clear();
+ const int * currentLayout = buttonLayout(Qt::Horizontal);
+ while (*currentLayout != QPlatformDialogHelper::EOL) {
+ int role = (*currentLayout & ~QPlatformDialogHelper::Reverse);
+ addButtons(opt, static_cast<ButtonRole>(role));
+ ++currentLayout;
+ }
+
+ QString text = htmlText(opt->text());
+ QString str = htmlText(opt->informativeText());
+ if (!str.isEmpty())
+ text += str;
+ str = htmlText(opt->detailedText());
+ if (!str.isEmpty())
+ text += str;
+
+ m_shown = true;
+ return true;
+}
+
+void QOhPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDialogOptions> opt, ButtonRole role)
+{
+ for (const QMessageDialogOptions::CustomButton &b : opt->customButtons()) {
+ if (b.role == role) {
+ QString label = b.label;
+ label.remove(QChar('&'));
+ m_buttons << label;
+ m_buttonIds << b.id;
+ }
+ }
+
+ for (int i = QPlatformDialogHelper::FirstButton; i < QPlatformDialogHelper::LastButton; i<<=1) {
+ StandardButton b = static_cast<StandardButton>(i);
+ if (buttonRole(b) == role && (opt->standardButtons() & i)) {
+ const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(b);
+ m_buttons << text;
+ m_buttonIds << i;
+ }
+ }
+}
+
+void QOhPlatformMessageDialogHelper::hide()
+{
+ m_shown = false;
+}
+
+void QOhPlatformMessageDialogHelper::setDialogResult(int buttonID)
+{
+ m_buttonId = buttonID;
+ if (m_buttonId < 0) {
+ emit reject();
+ return;
+ }
+
+ int realId = m_buttonIds.at(m_buttonId);
+ QPlatformDialogHelper::StandardButton standardButton = static_cast<QPlatformDialogHelper::StandardButton>(realId);
+ QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::buttonRole(standardButton);
+ if (buttonID > QPlatformDialogHelper::LastButton) {
+ const QMessageDialogOptions::CustomButton *custom = options()->customButton(buttonID);
+ Q_ASSERT(custom);
+ role = custom->role;
+ }
+ emit clicked(standardButton, role);
+}
+
+class OhFileData
+{
+public:
+ OhFileData() : m_data(new Data) {}
+ void fromOptions(const QSharedPointer<QFileDialogOptions> &o);
+
+ QUrl directory() const;
+ void setDirectory(const QUrl &);
+ QString selectedNameFilter() const;
+ void setSelectedNameFilter(const QString &);
+ QList<QUrl> selectedFiles() const;
+ void setSelectedFiles(const QList<QUrl> &);
+ void setResult(const QStringList &files);
+ QString selectedFile() const;
+private:
+ class Data : public QSharedData {
+ public:
+ QUrl directory;
+ QString selectedNameFilter;
+ QList<QUrl> selectedFiles;
+ QMutex mutex;
+ };
+ QExplicitlySharedDataPointer<Data> m_data;
+};
+
+inline QUrl OhFileData::directory() const
+{
+ m_data->mutex.lock();
+ const QUrl result = m_data->directory;
+ m_data->mutex.unlock();
+ return result;
+}
+
+inline void OhFileData::setDirectory(const QUrl &d)
+{
+ QMutexLocker locker(&m_data->mutex);
+ m_data->directory = d;
+}
+
+inline QString OhFileData::selectedNameFilter() const
+{
+ m_data->mutex.lock();
+ const QString result = m_data->selectedNameFilter;
+ m_data->mutex.unlock();
+ return result;
+}
+
+inline void OhFileData::setSelectedNameFilter(const QString &f)
+{
+ QMutexLocker locker(&m_data->mutex);
+ m_data->selectedNameFilter = f;
+}
+
+inline QList<QUrl> OhFileData::selectedFiles() const
+{
+ m_data->mutex.lock();
+ const auto result = m_data->selectedFiles;
+ m_data->mutex.unlock();
+ return result;
+}
+
+inline QString OhFileData::selectedFile() const
+{
+ const auto files = selectedFiles();
+ return files.isEmpty() ? QString() : files.front().toLocalFile();
+}
+
+inline void OhFileData::setSelectedFiles(const QList<QUrl> &urls)
+{
+ QMutexLocker locker(&m_data->mutex);
+ m_data->selectedFiles = urls;
+}
+
+void OhFileData::setResult(const QStringList &files)
+{
+ QList<QUrl> result;
+ std::transform(files.constBegin(), files.constEnd(), std::back_inserter(result), [](const QString &url){
+ return QUrl(url);
+ });
+ setSelectedFiles(result);
+}
+
+inline void OhFileData::fromOptions(const QSharedPointer<QFileDialogOptions> &o)
+{
+ QMutexLocker locker(&m_data->mutex);
+ m_data->directory = o->initialDirectory();
+ m_data->selectedFiles = o->initiallySelectedFiles();
+ m_data->selectedNameFilter = o->initiallySelectedNameFilter();
+}
+
+class QOhNativeFileDialog : public QOhNativeDialogBase
+{
+ Q_OBJECT
+public:
+ enum class OHSelectMode {
+ FILE,
+ FOLDER,
+ MIXED
+ };
+ explicit QOhNativeFileDialog(const OhFileData &data) : m_open(false), m_maxNumber(1), m_data(data) {}
+ inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::AcceptMode acceptMode, QFileDialogOptions::FileDialogOptions options);
+ inline void setDirectory(const QUrl &directory);
+ inline void updateDirectory() { setDirectory(m_data.directory()); }
+ inline QString directory() const;
+ virtual void setNameFilters(const QStringList &f);
+ void selectNameFilter(const QString &filter);
+ void setUserSelectedNameFilter(int index);
+ inline void updateSelectedNameFilter() { selectNameFilter(m_data.selectedNameFilter()); }
+ inline QString selectedNameFilter() const;
+ void selectFile(const QString &fileName);
+
+protected:
+ void doExec(QWindow *parent);
+ const OhFileData &data() const { return m_data; }
+ OhFileData &data() { return m_data; }
+
+private:
+ std::pair<Napi::Value, Napi::Value> validContextAndWindow(QWindow *window) const;
+ std::shared_ptr<QJsObject> documentPicker(QWindow *parent) const;
+ OHSelectMode qtMode2OhMode(QFileDialogOptions::FileMode mode);
+ int selectMaxSelectNumber(QFileDialogOptions::FileMode mode);
+ Napi::Value documentSelectOptions() const;
+ Napi::Value documentSaveOptions() const;
+ void open(QWindow *parent);
+ void save(QWindow *parent);
+private:
+ bool m_open;
+ OHSelectMode m_mode;
+ int m_maxNumber;
+ QString m_saveFile;
+ QStringList m_nameFilters;
+ OhFileData m_data;
+ QEventLoop m_eventLoop;
+};
+
+static QString qt_make_filter_for_openharmony(const QString &filter)
+{
+ QString _filter = filter;
+ QString description = "|";
+ QStringList parts;
+ if (_filter.contains("(") && _filter.contains(")")) {
+ parts = _filter.split("(");
+ _filter = parts.takeLast();
+ _filter.chop(1);
+ description.prepend(parts.join("(") + "("+ _filter + ")");
+ } else {
+ description.prepend(_filter);
+ }
+ parts = _filter.split(" ");
+ for (QString &part : parts) {
+ if (part.startsWith("*.")) {
+ /*
+ * 移除开头的 '*'
+ * 说明: 鸿蒙系统的文件选择器,只接受 '.png' 这种格式的后缀
+ */
+ part.remove(0, 1);
+ }
+ else if(!part.contains(".") ) {
+ part.prepend(".");
+ }
+ }
+ /*
+ * 连接描述和过滤器部分
+ *
+ * 说明: 鸿蒙系统的文件选择器
+ * 每项过滤后缀可以存在多个后缀名
+ * 则每一个后缀名之间用英文逗号进行分隔
+ */
+ return description + parts.join(",");
+}
+
+static QStringList qt_make_filter_list_for_openharmony(const QStringList &filters)
+{
+ QStringList allExtensions;
+ if (filters.count() == 1 && filters.first() == QFileDialogOptions::defaultNameFilterString()){
+ allExtensions << ".*";
+ } else {
+ for (auto _filter : filters) {
+ allExtensions << qt_make_filter_for_openharmony(_filter);
+ }
+ }
+ return allExtensions;
+}
+
+QOhNativeFileDialog::OHSelectMode QOhNativeFileDialog::qtMode2OhMode(QFileDialogOptions::FileMode mode)
+{
+ switch (mode) {
+ case QFileDialogOptions::AnyFile:
+ case QFileDialogOptions::ExistingFile:
+ case QFileDialogOptions::ExistingFiles:
+ break;
+ case QFileDialogOptions::Directory:
+ return OHSelectMode::FOLDER;
+ case QFileDialogOptions::DirectoryOnly:
+ return OHSelectMode::FOLDER;
+ default:
+ break;
+ }
+ return OHSelectMode::FILE;
+}
+
+int QOhNativeFileDialog::selectMaxSelectNumber(QFileDialogOptions::FileMode mode)
+{
+ switch (mode) {
+ case QFileDialogOptions::ExistingFiles:
+ return 500;
+ case QFileDialogOptions::ExistingFile:
+ default:
+ break;
+ }
+ return 1;
+}
+
+void QOhNativeFileDialog::setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::AcceptMode acceptMode, QFileDialogOptions::FileDialogOptions options)
+{
+ m_open = acceptMode == QFileDialogOptions::AcceptOpen;
+ m_mode = qtMode2OhMode(mode);
+ m_maxNumber = selectMaxSelectNumber(mode);
+}
+
+void QOhNativeFileDialog::setDirectory(const QUrl &directory)
+{
+ Q_UNUSED(directory)
+ Q_UNIMPLEMENTED();
+}
+
+void QOhNativeFileDialog::setNameFilters(const QStringList &f)
+{
+ m_nameFilters = f;
+}
+
+void QOhNativeFileDialog::selectNameFilter(const QString &filter)
+{
+ QString ohFilter = qt_make_filter_for_openharmony(filter);
+ Q_UNUSED(ohFilter)
+ Q_UNIMPLEMENTED();
+}
+
+void QOhNativeFileDialog::selectFile(const QString &fileName)
+{
+ m_saveFile = fileName;
+}
+
+void QOhNativeFileDialog::setUserSelectedNameFilter(int index)
+{
+ if (index >= 0 && index < m_nameFilters.count()) {
+ const QString filter = m_nameFilters.at(index);
+ if (!filter.isEmpty())
+ m_data.setSelectedNameFilter(filter);
+ }
+}
+
+void QOhNativeFileDialog::doExec(QWindow *parent)
+{
+ if (m_open)
+ open(parent);
+ else
+ save(parent);
+ m_eventLoop.exec();
+}
+
+std::pair<Napi::Value, Napi::Value> QOhNativeFileDialog::validContextAndWindow(QWindow *window) const
+{
+ QWindow *_window = window;
+ if (_window == nullptr)
+ _window = QGuiApplication::focusWindow();
+ if (_window == nullptr) {
+ _window = QGuiApplication::topLevelWindows().empty() ? nullptr : QGuiApplication::topLevelWindows().last();
+ }
+ if (_window == nullptr)
+ return {};
+
+ QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
+ QJsObject *nativeWindow = static_cast<QJsObject *>(platform->nativeResourceForWindow("nativeWindow", _window));
+ if (nativeWindow == nullptr)
+ return {};
+ QJsContext *context = qNativeWindowManager->context(_window);
+ return {context->object(), nativeWindow->object()};
+}
+
+std::shared_ptr<QJsObject> QOhNativeFileDialog::documentPicker(QWindow *parent) const
+{
+ static QJsModule picker("@ohos.file.picker");
+ Napi::Value documentPickerConstructor = picker.get("DocumentViewPicker");
+ auto args = validContextAndWindow(parent);
+ return std::make_shared<QJsObject>(documentPickerConstructor, std::initializer_list<napi_value>{args.first, args.second});
+}
+
+Napi::Value QOhNativeFileDialog::documentSelectOptions() const
+{
+ static QJsModule picker("@ohos.file.picker");
+ QStringList _filters = qt_make_filter_list_for_openharmony(m_nameFilters);
+ if (_filters.length() >= 100 || _filters.isEmpty()) {
+ _filters = QStringList() << ".*";
+ }
+ Napi::Value documentSelectOptionsConstructor = picker.get("DocumentSelectOptions");
+ QJsObject documentSelectOptions(documentSelectOptionsConstructor);
+ documentSelectOptions.set("fileSuffixFilters", QNapi::create(_filters));
+ documentSelectOptions.set("selectMode", m_mode);
+ documentSelectOptions.set("maxSelectNumber", m_maxNumber);
+ const QString path = m_data.directory().toLocalFile();
+ QString defaultPath = QString("%1/%2").arg(path, m_data.selectedFile());
+ if (!QFile::exists(defaultPath)) {
+ defaultPath = path;
+ }
+
+ defaultPath = QDir::cleanPath(defaultPath);
+ QString data = QtOh::uriFromPath(defaultPath);
+ if (!data.isEmpty()) {
+ documentSelectOptions.set("defaultFilePathUri", data);
+ }
+ return documentSelectOptions.object();
+}
+
+Napi::Value QOhNativeFileDialog::documentSaveOptions() const
+{
+ static QJsModule picker("@ohos.file.picker");
+ Napi::Value documentSaveOptionsConstructor = picker.get("DocumentSaveOptions");
+ QJsObject documentSaveOptions(documentSaveOptionsConstructor);
+ documentSaveOptions.set("newFileNames", QNapi::create(QStringList() << m_saveFile));
+ documentSaveOptions.set("fileSuffixChoices", QNapi::create(qt_make_filter_list_for_openharmony(m_nameFilters)));
+ const QString path = m_data.directory().toLocalFile();
+ QString defaultPath = QString("%1/%2").arg(path, m_saveFile);
+ if (!QFile::exists(defaultPath)) {
+ defaultPath = path;
+ }
+
+ defaultPath = QDir::cleanPath(defaultPath);
+ QString data = QtOh::uriFromPath(defaultPath);
+ if (!data.isEmpty()) {
+ documentSaveOptions.set("defaultFilePathUri", data);
+ }
+ return documentSaveOptions.object();
+}
+
+void QOhNativeFileDialog::open(QWindow *parent)
+{
+ auto callJsFunc = [this, parent]{
+ auto documentPicker = this->documentPicker(parent);
+ Napi::Promise result = documentPicker->call("select", documentSelectOptions()).As<Napi::Promise>();
+ QJsPromise promise(result);
+ promise.onThen([this](const Napi::CallbackInfo& info) {
+ QEventLoopLocker locker(&m_eventLoop);
+ if (info.Length() < 1) {
+ return;
+ }
+ QStringList filePaths = QNapi::getFirst<QStringList>(info);
+ if (!filePaths.isEmpty()) {
+ m_data.setResult(filePaths);
+ emit accepted();
+ } else {
+ emit rejected();
+ }
+ }).onCatch([this](const Napi::CallbackInfo& info){
+ Q_UNUSED(info)
+ QEventLoopLocker locker(&m_eventLoop);
+ emit rejected();
+ });
+ };
+
+ QtOh::runOnJsUIThreadNoWait(callJsFunc);
+}
+
+void QOhNativeFileDialog::save(QWindow *parent)
+{
+ auto callJsFunc = [this, parent]{
+ auto documentPicker = this->documentPicker(parent);
+ Napi::Promise result = documentPicker->call("save", documentSaveOptions()).As<Napi::Promise>();
+ QJsPromise promise(result);
+ promise.onThen([this, documentPicker](const Napi::CallbackInfo& info){
+ QEventLoopLocker locker(&m_eventLoop);
+ if (info.Length() < 1) {
+ return;
+ }
+ int index = QNapi::get<int>(documentPicker->call("getSelectedIndex"));
+ setUserSelectedNameFilter(index);
+ QStringList filePaths = QNapi::getFirst<QStringList>(info);
+ if (!filePaths.isEmpty()) {
+ m_data.setResult(filePaths);
+ emit accepted();
+ } else {
+ emit rejected();
+ }
+ }).onCatch([this](const Napi::CallbackInfo& info){
+ Q_UNUSED(info)
+ QEventLoopLocker locker(&m_eventLoop);
+ emit rejected();
+ });
+ };
+
+ QtOh::runOnJsUIThreadNoWait(callJsFunc);
+}
+
+class QOhPlatformFileDialogHelper: public QOhDialogHelperBase<QPlatformFileDialogHelper>
+{
+ Q_OBJECT
+public:
+ virtual bool defaultNameFilterDisables() const override { return true; }
+ virtual void setDirectory(const QUrl &directory) override;
+ virtual QUrl directory() const override;
+ virtual void selectFile(const QUrl &filename) override;
+ virtual QList<QUrl> selectedFiles() const override;
+ virtual QList<QUrl> selectedUris() const override;
+ virtual void setFilter() override;
+ virtual void selectNameFilter(const QString &filter) override;
+ virtual QString selectedNameFilter() const override;
+ virtual QOhNativeDialogBase *createNativeDialog() override;
+private:
+ inline QOhNativeFileDialog *nativeFileDialog() const
+ { return static_cast<QOhNativeFileDialog *>(nativeDialog()); }
+ OhFileData m_data;
+};
+
+void QOhPlatformFileDialogHelper::setDirectory(const QUrl &directory)
+{
+ m_data.setDirectory(directory);
+ if (hasNativeDialog())
+ nativeFileDialog()->updateDirectory();
+}
+
+QUrl QOhPlatformFileDialogHelper::directory() const
+{
+ return m_data.directory();
+}
+
+void QOhPlatformFileDialogHelper::selectFile(const QUrl &filename)
+{
+ QFileInfo fi(filename.toLocalFile());
+ QString saveFile = fi.fileName();
+ if (saveFile.contains("/")) {
+ int index = saveFile.lastIndexOf("/");
+ saveFile = saveFile.right(saveFile.length() - index - 1);
+ }
+
+ if (hasNativeDialog())
+ nativeFileDialog()->selectFile(saveFile);
+}
+
+QList<QUrl> QOhPlatformFileDialogHelper::selectedFiles() const
+{
+ auto files = m_data.selectedFiles();
+ QList<QUrl> result;
+ for (auto file: files) {
+ const QString &path = file.toString();
+ QString data = QtOh::pathFromUri(path);
+ if (!data.isEmpty()) {
+ result << QUrl::fromLocalFile(data);
+ }
+ }
+ return result;
+}
+
+QList<QUrl> QOhPlatformFileDialogHelper::selectedUris() const
+{
+ return m_data.selectedFiles();
+}
+
+void QOhPlatformFileDialogHelper::setFilter()
+{
+
+}
+
+void QOhPlatformFileDialogHelper::selectNameFilter(const QString &filter)
+{
+ m_data.setSelectedNameFilter(filter);
+ if (hasNativeDialog())
+ nativeFileDialog()->updateSelectedNameFilter();
+}
+
+QString QOhPlatformFileDialogHelper::selectedNameFilter() const
+{
+ return m_data.selectedNameFilter();
+}
+
+QOhNativeDialogBase *QOhPlatformFileDialogHelper::createNativeDialog()
+{
+ const QSharedPointer<QFileDialogOptions> &opts = options();
+ m_data.fromOptions(opts);
+ auto result = new QOhNativeFileDialog(m_data);
+ QObject::connect(result, &QOhNativeDialogBase::accepted, this, &QPlatformDialogHelper::accept);
+ QObject::connect(result, &QOhNativeDialogBase::rejected, this, &QPlatformDialogHelper::reject);
+ const QFileDialogOptions::FileMode mode = opts->fileMode();
+ result->setMode(mode, opts->acceptMode(), opts->options());
+ const QStringList nameFilters = opts->nameFilters();
+ if (!nameFilters.isEmpty())
+ result->setNameFilters(nameFilters);
+ result->updateDirectory();
+ result->updateSelectedNameFilter();
+ const QList<QUrl> initialSelection = opts->initiallySelectedFiles();
+ if (!initialSelection.empty()) {
+ const QUrl &url = initialSelection.constFirst();
+ if (url.isLocalFile()) {
+ QFileInfo info(url.toLocalFile());
+ if (!info.isDir())
+ result->selectFile(info.fileName());
+ } else {
+ result->selectFile(url.path());
+ }
+ }
+ if (mode != QFileDialogOptions::Directory && mode != QFileDialogOptions::DirectoryOnly) {
+ const QString initialNameFilter = opts->initiallySelectedNameFilter();
+ if (!initialNameFilter.isEmpty())
+ result->selectNameFilter(initialNameFilter);
+ }
+ return result;
+}
+
+template<class BaseClass>
+void QOhDialogHelperBase<BaseClass>::exec()
+{
+ if (QOhNativeDialogBase *nd = nativeDialog()) {
+ nd->exec(m_parent);
+ m_nativeDialog.clear();
+ }
+}
+
+template<class BaseClass>
+bool QOhDialogHelperBase<BaseClass>::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
+{
+ Q_UNUSED(windowFlags)
+ const bool modal = (windowModality != Qt::NonModal);
+ Q_UNUSED(modal);
+ m_parent = parent;
+ if (!ensureNativeDialog())
+ return false;
+ return true;
+}
+
+template<class BaseClass>
+void QOhDialogHelperBase<BaseClass>::hide()
+{
+// if (m_nativeDialog)
+// m_nativeDialog->hide();
+ m_parent = nullptr;
+}
+
+template <class BaseClass>
+QOhNativeDialogBase *QOhDialogHelperBase<BaseClass>::nativeDialog() const
+{
+ if (m_nativeDialog.isNull()) {
+ return nullptr;
+ }
+ return m_nativeDialog.data();
+}
+
+template <class BaseClass>
+QOhNativeDialogBase *QOhDialogHelperBase<BaseClass>::ensureNativeDialog()
+{
+ if (m_nativeDialog.isNull() || m_nativeDialog->executed())
+ m_nativeDialog = QOhNativeDialogBasePtr(createNativeDialog());
+ return m_nativeDialog.data();
+}
+
+QPlatformDialogHelper *QOhPlatformDialogHelpers::createHelper(QPlatformTheme::DialogType type)
+{
+ switch (type) {
+ // case QPlatformTheme::MessageDialog:
+ // return new QOhPlatformMessageDialogHelper;
+ case QPlatformTheme::FileDialog:
+ return new QOhPlatformFileDialogHelper();
+ default:
+ return nullptr;
+ }
+}
+
+QT_END_NAMESPACE
+#include "qohplatformdialoghelpers.moc"
new file mode 100644
@@ -0,0 +1,40 @@
+#ifndef QOHPLATFORMDIALOGHELPERS_H
+#define QOHPLATFORMDIALOGHELPERS_H
+
+#include <qpa/qplatformdialoghelper.h>
+#include <qpa/qplatformtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhNativeDialogBase;
+template <class BaseClass>
+class QOhDialogHelperBase : public BaseClass
+{
+ Q_DISABLE_COPY_MOVE(QOhDialogHelperBase)
+public:
+ using QOhNativeDialogBasePtr = QSharedPointer<QOhNativeDialogBase>;
+
+ void exec() override;
+ bool show(Qt::WindowFlags windowFlags,
+ Qt::WindowModality windowModality,
+ QWindow *parent) override;
+ void hide() override;
+
+protected:
+ QOhDialogHelperBase() = default;
+ QOhNativeDialogBase *nativeDialog() const;
+ inline bool hasNativeDialog() const { return static_cast<bool>(m_nativeDialog); } /* Qt6 only modify */
+
+private:
+ virtual QOhNativeDialogBase *createNativeDialog() = 0;
+ inline QOhNativeDialogBase *ensureNativeDialog();
+ QOhNativeDialogBasePtr m_nativeDialog;
+ QPointer<QWindow> m_parent;
+};
+
+namespace QOhPlatformDialogHelpers {
+ QPlatformDialogHelper *createHelper(QPlatformTheme::DialogType type);
+}
+
+QT_END_NAMESPACE
+#endif // QOHPLATFORMDIALOGHELPERS_H
new file mode 100644
@@ -0,0 +1,32 @@
+#include <QtCore/qvariant.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <qopenharmonydefines.h>
+#include "qohplatformforeignwindow.h"
+#include <qpa/qplatformscreen.h>
+#include "qohauxiliary.h"
+#include "qohwindownode.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhPlatformForeignWindow::QOhPlatformForeignWindow(QWindow *window, WId nativeHandle)
+ : QOhPlatformWindow(window)
+ , m_nativeHandle(nativeHandle)
+{
+ auto r = QtOh::densityPixels(window->screen()->handle());
+ QRect rect = window->geometry();
+ QPlatformWindow::setGeometry(QRect(rect.x() * r, rect.y() * r, rect.width() * r, rect.height() * r));
+}
+
+void QOhPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState state)
+{
+ QOhPlatformWindow::applicationStateChanged(state);
+}
+
+void QOhPlatformForeignWindow::initialize()
+{
+ QOhPlatformWindow::initialize();
+ createNativeWindowNode(m_nativeHandle);
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,22 @@
+#ifndef QOHPLATFORMFOREIGNWINDOW_H
+#define QOHPLATFORMFOREIGNWINDOW_H
+
+#include "qohplatformwindow.h"
+#include "qohmain.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformForeignWindow : public QOhPlatformWindow
+{
+public:
+ explicit QOhPlatformForeignWindow(QWindow *window, WId nativeHandle);
+ void applicationStateChanged(Qt::ApplicationState state) override;
+ bool isForeignWindow() const override { return true; }
+ void initialize() override;
+private:
+ WId m_nativeHandle;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMFOREIGNWINDOW_H
new file mode 100644
@@ -0,0 +1,578 @@
+#include "qohmain.h"
+#include "qohevent.h"
+#include "qohplatformscreen.h"
+#include "qohplatformwindow.h"
+#include "qohplatformintegration.h"
+#include "qohplatforminputcontext.h"
+#include "qohinputmethodproxy.h"
+#include "qohcontroller.h"
+#include "qohcursorinfo.h"
+#include "qohattachoptions.h"
+#include "qohtexteditorproxy.h"
+#include "qohinputmethod.h"
+#include "qohwindowcontext.h"
+#include "qohkeys.h"
+
+#include <QtCore/qopenharmonydefines.h>
+
+#include <QJsModule>
+#include <QPointer>
+#include <QTimer>
+#include <qevent.h>
+#include <qthread.h>
+#include <qwindow.h>
+#include <qmetaobject.h>
+#include <qinputmethod.h>
+#include <qsharedpointer.h>
+#include <qguiapplication.h>
+#include <private/qhighdpiscaling_p.h>
+
+#include <QTextCharFormat>
+#include <QTextBoundaryFinder>
+#include <qpa/qplatformcursor.h>
+#include <qpa/qplatformintegration.h>
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(ohim, "qt.ohos.inputcontext")
+
+static QOhPlatformInputContext *m_openHarmonyInputContext = 0;
+
+// cursor position getter that also works with editors that have not been updated to the new API
+static inline int getAbsoluteCursorPosition(const QSharedPointer<QInputMethodQueryEvent> &query)
+{
+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
+ return absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt();
+}
+
+// position of the start of the current block
+static inline int getBlockPosition(const QSharedPointer<QInputMethodQueryEvent> &query)
+{
+ QVariant absolutePos = query->value(Qt::ImAbsolutePosition);
+ return absolutePos.isValid() ? absolutePos.toInt() - query->value(Qt::ImCursorPosition).toInt() : 0;
+}
+
+void QOhPlatformInputContext::cursorRectangleChanged()
+{
+ if (!m_controller->isAttached()) {
+ //qCWarning(ohim) << "Attempting to update cursor position when detached from controller.";
+ return;
+ }
+
+ auto focusedWindow = QGuiApplication::focusWindow();
+ if (focusedWindow == nullptr) {
+ qCDebug(ohim) << "Could not retrieve focused window. Updating cursor position isn't possible";
+ return;
+ }
+
+ while (focusedWindow->parent()) {
+ focusedWindow = focusedWindow->parent();
+ }
+
+ if (!focusedWindow) {
+ qCDebug(ohim) << "Could not retrieve focused window. Updating cursor position isn't possible";
+ return;
+ }
+
+ auto position = focusedWindow->position();
+ auto platformScreen = focusedWindow->screen()->handle();
+ position -= platformScreen->geometry().topLeft();
+ auto focusedWindowPosition = QHighDpi::toNativeLocalPosition(position, focusedWindow);
+ QRect cursorRectangle = QHighDpi::toNativeLocalPosition(QGuiApplication::inputMethod()->cursorRectangle().toRect(), focusedWindow);
+ if (cursorRectangle == m_lastCursorRectangle &&
+ m_lastFocusedWindowPosition == focusedWindowPosition) {
+ return;
+ }
+
+ m_lastCursorRectangle = cursorRectangle;
+ m_lastFocusedWindowPosition = focusedWindowPosition;
+ auto screenSpaceInputItemRectangle = QHighDpi::toNativeLocalPosition(QGuiApplication::inputMethod()->inputItemClipRectangle(), focusedWindow)
+ .translated(m_lastFocusedWindowPosition).toRect();
+
+ auto screenSpaceCursorRectangle = m_lastCursorRectangle.translated(m_lastFocusedWindowPosition);
+
+ auto clampToRect = [](const QPoint &p, const QRect &rect)->QPoint {
+ int x = qBound(rect.left(), p.x(), rect.right());
+ int y = qBound(rect.top(), p.y(), rect.bottom());
+ return QPoint(x, y);
+ };
+
+ auto inputItemClampedCursorPos = clampToRect( {screenSpaceCursorRectangle.x(),
+ screenSpaceCursorRectangle.y() },
+ screenSpaceInputItemRectangle.isValid()
+ ? screenSpaceInputItemRectangle
+ : focusedWindow->geometry());
+
+ InputMethod::QOhCursorInfo cursorInfo = InputMethod::QOhCursorInfo(inputItemClampedCursorPos.x(),
+ inputItemClampedCursorPos.y(),
+ screenSpaceCursorRectangle.width(),
+ screenSpaceCursorRectangle.height());
+ if (m_controller->inputMethodProxy())
+ m_controller->inputMethodProxy()->notifyCursorUpdate(cursorInfo);
+}
+
+void QOhPlatformInputContext::updateCursor()
+{
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (query.isNull())
+ return;
+
+ bool imEnable = query->queries().testFlag(Qt::ImEnabled);
+ if (!imEnable)
+ return;
+
+/* FIXME 暂时注释这里和QInputMethod::cursorRectangleChanged响应的输入位置更新
+ * 因为会导致点击应用内界面,输入法位置乱跳
+ */
+#if 0
+ if (m_controller->isAttached()) {
+ cursorRectangleChanged();
+ }
+#endif
+}
+
+QOhPlatformInputContext::QOhPlatformInputContext()
+ : QPlatformInputContext()
+ , m_state(0)
+{
+ m_controller.reset(new InputMethod::QOhController());
+ auto *textEditorProxy = m_controller->textEditorProxy();
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::keyboardVisibleChanged, this, &QOhPlatformInputContext::emitInputPanelVisibleChanged);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::updateCursor, this, &QOhPlatformInputContext::updateCursor);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::commitText, this, &QOhPlatformInputContext::commitText);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::preeditText, this, &QOhPlatformInputContext::preeditText);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::enterKeyDown, this, &QOhPlatformInputContext::enterKeyDown);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::deleteBackward, this, &QOhPlatformInputContext::deleteBackward);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::deleteForward, this, &QOhPlatformInputContext::deleteForward);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::moveCursor, this, &QOhPlatformInputContext::commitMoveCursor);
+ connect(textEditorProxy, &InputMethod::QOhTextEditorProxy::finishTextPreview, this, &QOhPlatformInputContext::finishTextPreview);
+
+ m_openHarmonyInputContext = this;
+
+ connect(qApp, &QGuiApplication::focusWindowChanged,
+ this, &QOhPlatformInputContext::focusWindowChanged);
+ qRegisterMetaType<QtOh::KeyEvent>("QtOh::KeyEvent");
+}
+
+QOhPlatformInputContext::~QOhPlatformInputContext()
+{
+ m_openHarmonyInputContext = Q_NULLPTR;
+}
+/*!
+ * \brief 检查输入上下文是否具有指定的功能
+ * \param capability 枚举值,用于指定功能类型
+ * \return 返回布尔值,如果具有指定功能,则返回 true,否则返回 false
+ */
+bool QOhPlatformInputContext::hasCapability(Capability capability) const
+{
+ switch (capability) {
+ /* Note 鸿蒙支持隐藏文本(密码)输入 */
+ case HiddenTextCapability:
+ return true;
+ default:break;
+ }
+ return true;
+}
+/*!
+ * \brief 重置输入法状态,将输入法状态恢复到默认状态
+ */
+void QOhPlatformInputContext::reset()
+{
+ finishTextPreview();
+ m_lastCursorRectangle = QRect();
+ m_lastFocusedWindowPosition = QPoint();
+}
+/*!
+ * \brief 提交当前输入法状态,将当前输入法状态应用到文本编辑器中
+ */
+void QOhPlatformInputContext::commit()
+{
+
+}
+/*!
+ * \brief 编辑器更新通知。由QInputMethod::update()调用
+ * 函数的作用是更新输入上下文的状态,以便与应用程序的输入方法通信
+ * 并响应输入法相关的查询,一般流程如下:
+ * 1.当输入法状态发生变化,或者焦点对象发生变化时
+ * QGuiApplication 会调用当前活动的 QPlatformInputContext 对象的 update 函数
+ *
+ * 2.在 update 函数内部,QPlatformInputContext 可以查询焦点对象的相关信息,
+ * 例如文本内容、光标位置等,然后将这些信息传递给输入法对象
+ *
+ * 3.输入法对象根据接收到的信息更新其状态,并相应地调整显示内容或行为
+ * 4.用户与输入法进行交互,输入文本或者调整输入法的设置
+ * 5.当用户输入完成或者输入法状态发生变化时,输入法对象可能会触发输入法事件,
+ * 通知 QPlatformInputContext 对象更新状态
+ */
+void QOhPlatformInputContext::update(Qt::InputMethodQueries queries)
+{
+ if (Qt::ImEnabled & queries)
+ updateInputEnabled();
+
+ if (Qt::ImCursorRectangle & queries)
+ cursorRectangleChanged();
+}
+
+/*!
+ * \brief 调用输入法的动作,以执行指定的操作,如向前移动光标、向后删除等
+ * \param act 动作类型
+ * \param cursorPosition 光标位置
+ */
+void QOhPlatformInputContext::invokeAction(QInputMethod::Action act, int cursorPosition)
+{
+#warning TODO Handle
+ Q_UNUSED(act);
+ Q_UNUSED(cursorPosition);
+}
+/*!
+ * \brief 过滤输入事件,以便输入上下文可以处理输入事件并相应地调整输入法状态
+ * \param event 输入事件
+ * \return 返回布尔值
+ */
+bool QOhPlatformInputContext::filterEvent(const QEvent *event)
+{
+ if ((event->type() == QEvent::KeyRelease) && m_controller->isAttached()) {
+ QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(const_cast<QEvent *>(event));
+ auto key = keyEvent->key();
+ if ((key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left || key == Qt::Key_Right)) {
+ cursorRectangleChanged();
+ }
+ }
+ return QPlatformInputContext::filterEvent(event);
+}
+
+void QOhPlatformInputContext::setKeyboardRect(const QRect &rect)
+{
+ bool changed = m_keyboardRect != rect;
+ if (!changed)
+ return;
+ m_keyboardRect = rect;
+ emitKeyboardRectChanged();
+}
+/*!
+ * \brief 获取键盘区域的矩形范围,以便应用程序可以根据键盘的显示状态调整布局
+ * \return 返回键盘区域矩形大小
+ */
+QRectF QOhPlatformInputContext::keyboardRect() const
+{
+ return m_keyboardRect;
+}
+
+/*!
+ * \brief 返回布尔值,指示输入面板是否处于动画状态
+ * \return 返回布尔值
+ */
+bool QOhPlatformInputContext::isAnimating() const
+{
+ return false;
+}
+/*!
+ * \brief 显示输入面板(软键盘)
+ */
+void QOhPlatformInputContext::showInputPanel()
+{
+ if (!inputMethodAccepted())
+ return;
+
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (query.isNull())
+ return;
+
+ bool imEnable = query->queries().testFlag(Qt::ImEnabled);
+ if (!imEnable)
+ return;
+ if (!m_controller->isAttached()) {
+ m_controller->attach(InputMethod::QOhAttachOptions(true
+ #if OHOS_SDK_VERSION > 14
+ , m_requestKeyboardReason
+ #endif
+ ));
+
+ }
+ /*attach可能失败,失败时inputMethodProxy为nullptr*/
+ if (m_controller->isAttached()) {
+ m_controller->inputMethodProxy()->notifyConfigurationChanged(Qt::EnterKeyType(query->value(Qt::ImEnterKeyType).toInt()),
+ Qt::InputMethodHints(query->value(Qt::ImHints).toInt()));
+ if (!isInputPanelVisible()) {
+#if OHOS_SDK_VERSION > 14
+ m_controller->inputMethodProxy()->showTextInput(InputMethod::QOhAttachOptions(true, m_requestKeyboardReason));
+#endif
+ m_controller->inputMethodProxy()->showKeyboard();
+ cursorRectangleChanged();
+ }
+ }
+}
+/*!
+ * \brief 隐藏输入面板
+ */
+void QOhPlatformInputContext::hideInputPanel()
+{
+ if (m_controller->isAttached()) {
+ reset();
+ /* NOTE: 清空焦点对象,确保下次显示时能正确重新触发输入法 */
+ m_focusObject.clear();
+ if (m_controller->textEditorProxy()->isKeyboardVisible() && m_controller->inputMethodProxy())
+ m_controller->inputMethodProxy()->hideKeyboard();
+ m_controller->detach();
+ setRequestKeyboardReason(InputMethod::RequestKeyboardReason::NONE);
+ }
+}
+/*!
+ * \brief 返回布尔值,指示输入面板的可见性状态
+ * \return 返回布尔值
+ */
+bool QOhPlatformInputContext::isInputPanelVisible() const
+{
+ bool visible = m_controller->textEditorProxy()->isKeyboardVisible();
+ return visible;
+}
+
+/*!
+ * \brief 设置获得焦点的对象
+ * \param object 对象指针
+ */
+void QOhPlatformInputContext::setFocusObject(QObject *object)
+{
+ Q_UNUSED(object)
+ if (m_isInPreviewText)
+ reset();
+ /* NOTE: 清空之前的焦点对象,确保每次焦点变化都能正确触发输入法 */
+ m_focusObject.clear();
+ QTimer::singleShot(0, this, [this]{ updateInputEnabled(); });
+}
+
+QOhPlatformInputContext *QOhPlatformInputContext::ohInputContext()
+{
+ return m_openHarmonyInputContext;
+}
+
+void QOhPlatformInputContext::clear()
+{
+ m_ignoreMouseEvents = false;
+}
+
+bool QOhPlatformInputContext::ignoreMouse() const
+{
+ return m_ignoreMouseEvents;
+}
+
+/*!
+ * \brief FIXME 临时方法发送EnterKey按键事件
+ */
+void QOhPlatformInputContext::enterKeyDown()
+{
+ LOGW("<--------------------- enterKeyDown ----------------->");
+ QObject *input = qGuiApp->focusObject();
+ if(!input)
+ return;
+ QKeyEvent *k1 = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, KEY_ENTER, KEY_ENTER, KEY_ENTER);
+ QGuiApplication::postEvent(input, k1);
+
+ QKeyEvent *k2 = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier, KEY_ENTER, KEY_ENTER, KEY_ENTER);
+ QGuiApplication::postEvent(input, k2);
+}
+
+void QOhPlatformInputContext::deleteBackward()
+{
+ Qt::KeyboardModifiers mods = QOhKeys::keyboardModifiers();
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyPress, Qt::Key_Backspace,
+ mods, KEY_DEL, KEY_DEL, int(mods));
+
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyRelease, Qt::Key_Backspace,
+ mods, KEY_DEL, KEY_DEL, int(mods));
+ cursorRectangleChanged();
+
+}
+
+void QOhPlatformInputContext::deleteForward()
+{
+ Qt::KeyboardModifiers mods = QOhKeys::keyboardModifiers();
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyPress, Qt::Key_Delete,
+ mods, KEY_FORWARD_DEL, KEY_FORWARD_DEL, int(mods));
+
+ QWindowSystemInterface::handleExtendedKeyEvent(QOhWindowContext::mouseGrabberWindow(),
+ QEvent::KeyRelease, Qt::Key_Delete,
+ mods, KEY_FORWARD_DEL, KEY_FORWARD_DEL, int(mods));
+ cursorRectangleChanged();
+
+}
+
+void QOhPlatformInputContext::commitText(const QString &text)
+{
+ if (!m_controller->isAttached())
+ return;
+ QObject *input = checkFocusObject();
+
+ if (input && !text.isEmpty()) {
+
+ QInputMethodQueryEvent query(Qt::ImCursorPosition);
+ QCoreApplication::sendEvent(input, &query);
+ QVariant posValue = query.value(Qt::ImCursorPosition);
+ int cursorPosition = 0;
+ if (!posValue.isNull()) {
+ cursorPosition = posValue.toInt();
+ }
+
+ QInputMethodEvent event;
+ event.setCommitString(text);
+ QCoreApplication::sendEvent(input, &event);
+ cursorRectangleChanged();
+ if (m_controller->inputMethodProxy())
+ m_controller->inputMethodProxy()->notifySelectionChanged(text, cursorPosition, cursorPosition + text.length());
+ m_isInPreviewText = false;
+ m_previewText = QString();
+ }
+}
+
+void QOhPlatformInputContext::preeditText(const QString &text)
+{
+ QObject *input = checkFocusObject();
+ if (!input)
+ return;
+
+ m_isInPreviewText = true;
+ m_previewText = text;
+ QInputMethodQueryEvent query(Qt::ImAbsolutePosition);
+ QCoreApplication::sendEvent(input, &query);
+ int cursorPos = query.value(Qt::ImAbsolutePosition).toInt();
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ format.setUnderlineColor(Qt::gray);
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(
+ QInputMethodEvent::TextFormat,
+ 0,
+ text.length(),
+ format
+ ));
+ attributes.append(QInputMethodEvent::Attribute(
+ QInputMethodEvent::Cursor,
+ text.length(),
+ 1,
+ QVariant()
+ ));
+ QInputMethodEvent event(text, attributes);
+
+ QCoreApplication::sendEvent(input, &event);
+}
+
+void QOhPlatformInputContext::commitMoveCursor(int direction)
+{
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (query.isNull())
+ return;
+ /* Qt 6 only modify */
+ QScopedPointer<QInputMethodEvent> event;
+ const int absolutecursorPos = getAbsoluteCursorPosition(query);
+ /* 1-up 2-down
+ * 3-left 4-right
+ */
+ switch(direction)
+ {
+ case 1:
+ break;
+ case 2:
+ break;
+ case 3:
+ event.reset(new QInputMethodEvent({}, { { QInputMethodEvent::Selection, qMax(absolutecursorPos - 1, 0), 0 } }));
+ break;
+ case 4:
+ event.reset(new QInputMethodEvent ({}, { { QInputMethodEvent::Selection, absolutecursorPos + 1, 0 } }));
+ break;
+ default:break;
+ }
+
+ auto input = checkFocusObject();
+ if (input)
+ QGuiApplication::sendEvent(input, event.data());
+}
+
+void QOhPlatformInputContext::finishTextPreview()
+{
+ if (m_isInPreviewText) {
+ if (!m_focusObject.isNull() && !m_previewText.isEmpty()) {
+ QInputMethodEvent event;
+ event.setCommitString(m_previewText);
+ QCoreApplication::sendEvent(m_focusObject.data(), &event);
+ m_previewText = QString();
+ }
+ m_isInPreviewText = false;
+ }
+}
+
+void QOhPlatformInputContext::showInputPanelLater(Qt::ApplicationState state)
+{
+ if (state != Qt::ApplicationActive)
+ return;
+ disconnect(qGuiApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(showInputPanelLater(Qt::ApplicationState)));
+ showInputPanel();
+}
+
+void QOhPlatformInputContext::focusWindowChanged(QWindow *focusWindow)
+{
+ if (focusWindow == nullptr) {
+ if (m_controller->isAttached()) {
+ if (m_controller->textEditorProxy()->isKeyboardVisible() && m_controller->inputMethodProxy())
+ m_controller->inputMethodProxy()->hideKeyboard();
+ m_controller->detach();
+ }
+ }
+}
+
+QObject *QOhPlatformInputContext::checkFocusObject()
+{
+ QObject *input = qGuiApp->focusObject();
+ if (!input)
+ return nullptr;
+ if (m_focusObject.isNull() || m_focusObject.data() != input)
+ m_focusObject = input;
+ return input;
+}
+
+void QOhPlatformInputContext::sendInputMethodEvent(QInputMethodEvent *event)
+{
+ if (!qGuiApp)
+ return;
+
+ QObject *focusObject = qGuiApp->focusObject();
+ if (!focusObject)
+ return;
+
+ QCoreApplication::sendEvent(focusObject, event);
+}
+
+void QOhPlatformInputContext::setRequestKeyboardReason(InputMethod::RequestKeyboardReason reason)
+{
+ m_requestKeyboardReason = reason;
+}
+
+void QOhPlatformInputContext::updateInputEnabled()
+{
+ if (!QGuiApplication::focusObject())
+ return;
+
+ if (inputMethodAccepted() && !isInputPanelVisible()) {
+ showInputPanel();
+ }
+}
+
+QSharedPointer<QInputMethodQueryEvent> QOhPlatformInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries)
+{
+ if (!qGuiApp)
+ return {};
+
+ QObject *focusObject = qGuiApp->focusObject();
+ if (!focusObject)
+ return {};
+
+ QInputMethodQueryEvent *ret = new QInputMethodQueryEvent(queries);
+ QCoreApplication::sendEvent(focusObject, ret);
+ return QSharedPointer<QInputMethodQueryEvent>(ret);
+}
+
+QT_END_NAMESPACE
+
+
new file mode 100644
@@ -0,0 +1,96 @@
+#ifndef QOHPLATFORMINPUTCONTEXT_H
+#define QOHPLATFORMINPUTCONTEXT_H
+
+#include <QPointer>
+#include <cstdint>
+#include <napi/native_api.h>
+#include <qpa/qplatforminputcontext.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <multimodalinput/oh_input_manager.h>
+#include <ace/xcomponent/native_xcomponent_key_event.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+#include "qohinputmethod.h"
+
+QT_BEGIN_NAMESPACE
+class QWindow;
+namespace InputMethod
+{
+class QOhController;
+};
+
+namespace QtOh {
+class KeyEvent;
+}
+
+class QOhPlatformInputContext: public QPlatformInputContext
+{
+ Q_OBJECT
+
+public:
+ QOhPlatformInputContext();
+ ~QOhPlatformInputContext() override;
+
+ /* 判断输入上下文是否有效 */
+ bool isValid() const override { return true; }
+ bool hasCapability(Capability capability) const override;
+
+ void reset() override;
+ void commit() override;
+ void update(Qt::InputMethodQueries queries) override;
+ void invokeAction(QInputMethod::Action act, int cursorPosition) override;
+
+ bool filterEvent(const QEvent *event) override;
+
+ void setKeyboardRect(const QRect &rect);
+ QRectF keyboardRect() const override;
+
+ bool isAnimating() const override;
+
+ void showInputPanel() override;
+ void hideInputPanel() override;
+ bool isInputPanelVisible() const override;
+
+ void setFocusObject(QObject *object) override;
+
+ void clear();
+ bool ignoreMouse() const;
+ static QOhPlatformInputContext *ohInputContext();
+
+ void setRequestKeyboardReason(InputMethod::RequestKeyboardReason reason = InputMethod::RequestKeyboardReason::NONE);
+
+ void updateInputEnabled();
+
+private Q_SLOTS:
+ void cursorRectangleChanged();
+ void updateCursor();
+ void enterKeyDown();
+ void deleteBackward();
+ void deleteForward();
+ void commitText(const QString &text);
+ void preeditText(const QString &text);
+ void commitMoveCursor(int direction);
+ void finishTextPreview();
+ void showInputPanelLater(Qt::ApplicationState state);
+ void focusWindowChanged(QWindow *focusWindow);
+ QObject *checkFocusObject();
+private:
+ void sendInputMethodEvent(QInputMethodEvent *event);
+ QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
+
+private:
+ int m_state; /* 辅助键状态 */
+ QPointer<QObject> m_focusObject;
+ bool m_ignoreMouseEvents = false;
+ QScopedPointer<InputMethod::QOhController> m_controller;
+ bool m_isInPreviewText = false;
+ QString m_previewText;
+ /* 记录输入跟随位置 */
+ QRect m_lastCursorRectangle;
+ QPoint m_lastFocusedWindowPosition;
+ QRect m_keyboardRect;
+ InputMethod::RequestKeyboardReason m_requestKeyboardReason = InputMethod::RequestKeyboardReason::NONE;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMINPUTCONTEXT_H
new file mode 100644
@@ -0,0 +1,308 @@
+#include <QThread>
+#include <QMetaObject>
+#include <QOpenGLContext>
+#include <QGuiApplication>
+#include <QOffscreenSurface>
+#include <qohutility.h>
+#include <QtGui/private/qguiapplication_p.h>
+
+#include "qohdrag.h"
+#include "qoheventdispatcher.h"
+#include "qohplatformtheme.h"
+#include "qohdesktopwindow.h"
+#include "qabstracteventdispatcher.h"
+#include "qohplatformscreen.h"
+#include "qohplatformservices.h"
+#include "qohfileenginehandler.h"
+#include "qohplatformopenglcontext.h"
+#include "qohplatformclipboard.h"
+#include "qohplatformintegration.h"
+#include "qohplatforminputcontext.h"
+#include "qohplatformbackingstore.h"
+#include "qohplatformopenglwindow.h"
+#include <private/qohfontdatabase_p.h>
+#include "qohplatformforeignwindow.h"
+#include "qohplatformoffscreensurface.h"
+#include "qohplatformnativeinterface.h"
+#include "qohplatformabilityctrl.h"
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+#include "qohplatformaccessibility.h"
+#endif
+#endif
+
+#include <qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformcursor.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformoffscreensurface.h>
+#include <private/qeglpbuffer_p.h>
+#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
+#include "qjsscreen.h"
+#include "qjsdisplay.h"
+#include "qohkeys.h"
+
+#if QT_CONFIG(vulkan)
+#include "qohplatformvulkaninstance.h"
+#endif
+
+#include "qohmain.h"
+#if OHOS_SDK_VERSION > 13
+#include "qohdisplaymanager.h"
+#endif
+
+#include "qjsaccessmanager.h"
+#include "qohnativewindowmanager.h"
+#include "qopenharmonydefines.h"
+
+QT_BEGIN_NAMESPACE
+
+QOhPlatformIntegration::QOhPlatformIntegration(const QStringList ¶mList)
+ : m_harmonyFontDatabase(nullptr)
+{
+ Q_UNUSED(paramList);
+
+ m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (m_eglDisplay == EGL_NO_DISPLAY) {
+ LOGE("Could not open egl display, error code: %{public}d", eglGetError());
+ return;
+ }
+
+ EGLint major, minor;
+ if (!eglInitialize(m_eglDisplay, &major, &minor)) {
+ m_eglDisplay = EGL_NO_DISPLAY;
+ LOGE("Could not initialize egl display, error code: %{public}d", eglGetError());
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ LOGE("Could not bind GL_ES API, error code: %{public}d", eglGetError());
+ return;
+ }
+
+ m_harmonyFontDatabase.reset(new QOhFontDatabase);
+
+#if OHOS_SDK_VERSION > 13
+ auto displayMan = new QOhDisplayManager();
+ displayMan->init();
+ m_displayManager.reset(displayMan);
+ //m_displayManager->init();
+#else
+ m_display = new QJsDisplay();
+ m_display->initialize();
+ QJsScreen *s = m_display->defaultScreen();
+ m_primaryScreen.reset(new QOhPlatformScreen(s));
+ QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data());
+#endif
+ QPlatformCursor::setCapability(QPlatformCursor::OverrideCursor);
+
+ m_mainThread = QThread::currentThread();
+
+#ifndef QT_NO_CLIPBOARD
+ m_platformClipboard.reset(new QOhPlatformClipboard);
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ m_drag.reset(new QOhDrag);
+#endif
+ m_platformNativeInterface.reset(new QOhPlatformNativeInterface());
+ m_platformServices.reset(new QOhPlatformServices());
+ m_openHarmonyFileEngineHandler.reset(new QOhFileEngineHandler);
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+ m_accessibility.reset(new QOhPlatformAccessibility);
+#endif
+#endif
+}
+
+
+bool QOhPlatformIntegration::hasCapability(Capability cap) const
+{
+ switch (cap) {
+ // 目前没有接收系统的程序状态变化,applicationContext.on('applicationStateChange')不会触发
+ // Qt内部处理
+ case ApplicationState:
+ case SwitchableWidgetComposition:
+ return false;
+ default:
+ return true;
+ }
+}
+
+QPlatformBackingStore *QOhPlatformIntegration::createPlatformBackingStore(QWindow *window) const
+{
+ return new QOhPlatformBackingStore(window);
+}
+
+#ifndef QT_NO_OPENGL
+QPlatformOpenGLContext *QOhPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
+{
+ QSurfaceFormat format(context->format());
+ // EGLConfig *config = const_cast<EGLConfig *>(&m_eglConfig);
+ auto ctx = new QOhPlatformOpenGLContext(format, context->shareHandle(), m_eglDisplay, nullptr);
+ return ctx;
+}
+/* Qt6 only modify */
+# if QT_CONFIG(egl)
+QOpenGLContext *QOhPlatformIntegration::createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const
+{
+ return QEGLPlatformContext::createFrom<QOhPlatformOpenGLContext>(context, display, m_eglDisplay, shareContext);
+}
+#endif
+#endif
+
+QPlatformOffscreenSurface *QOhPlatformIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
+{
+ QSurfaceFormat format(surface->requestedFormat());
+
+ return new QEGLPbuffer(m_eglDisplay, format, surface);
+}
+
+QPlatformAccessCtrl *QOhPlatformIntegration::accessCtrl() const
+{
+ return QJsAccessManager::instance();
+}
+
+QPlatformAbilityCtrl *QOhPlatformIntegration::abilityCtrl() const
+{
+ return QOhPlatformAbilityCtrl::instance();
+}
+
+QPlatformWindow *QOhPlatformIntegration::createPlatformWindow(QWindow *window) const
+{
+ if (window->type() == Qt::Desktop) {
+ return new QOhDesktopWindow(window);
+ }
+ return new QOhPlatformOpenGLWindow(window);
+}
+
+QPlatformWindow *QOhPlatformIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
+{
+ return new QOhPlatformForeignWindow(window, nativeHandle);
+}
+
+QAbstractEventDispatcher *QOhPlatformIntegration::createEventDispatcher() const
+{
+ return new QOhEventDispatcher(const_cast<QOhPlatformIntegration*>(this));
+}
+
+QOhPlatformIntegration::~QOhPlatformIntegration()
+{
+ if (m_eglDisplay != EGL_NO_DISPLAY)
+ eglTerminate(m_eglDisplay);
+ QOhPlatformInputContext *context = qobject_cast<QOhPlatformInputContext *>(m_platformInputContext.data());
+ if (context != nullptr)
+ context->clear();
+#if OHOS_SDK_VERSION > 13
+#else
+ delete m_display;
+#endif
+}
+
+void QOhPlatformIntegration::initialize()
+{
+ const QString icStr = QPlatformInputContextFactory::requested();
+ if (icStr.isNull()) {
+ QOhPlatformInputContext *context = new QOhPlatformInputContext;
+ m_platformInputContext.reset(context);
+ }
+ else
+ m_platformInputContext.reset(QPlatformInputContextFactory::create(icStr));
+}
+
+Qt::KeyboardModifiers QOhPlatformIntegration::queryKeyboardModifiers() const
+{
+ return QOhKeys::keyboardModifiers();
+}
+
+QPlatformInputContext *QOhPlatformIntegration::inputContext() const
+{
+ return m_platformInputContext.data();
+}
+
+QPlatformNativeInterface *QOhPlatformIntegration::nativeInterface() const
+{
+ return m_platformNativeInterface.data();
+}
+
+QPlatformServices *QOhPlatformIntegration::services() const
+{
+ return m_platformServices.data();
+}
+
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+QPlatformAccessibility *QOhPlatformIntegration::accessibility() const
+{
+ return m_accessibility.data();
+}
+#endif
+#endif
+
+QVariant QOhPlatformIntegration::styleHint(StyleHint hint) const
+{
+ switch (hint) {
+ case PasswordMaskDelay:
+ return m_showPasswordEnabled ? 1500 : 0;
+ case ShowIsMaximized:
+ return false;
+ default:
+ return QPlatformIntegration::styleHint(hint);
+ }
+}
+
+Qt::WindowState QOhPlatformIntegration::defaultWindowState(Qt::WindowFlags flags) const
+{
+ // Don't maximize dialogs on OpenHarmony
+ if (flags & Qt::Dialog & ~Qt::Window) {
+ return Qt::WindowNoState;
+ }
+ if (flags & Qt::Dialog & ~Qt::Window) {
+ return Qt::WindowNoState;
+ }
+ if ((flags & Qt::WindowType_Mask) == Qt::Window) {
+ if ((QtOh::Utility::type() != QtOh::Utility::PC) && !QtOh::isFreeWindowEnable())
+ return Qt::WindowFullScreen;
+ }
+ return QPlatformIntegration::defaultWindowState(flags);
+}
+/* NOTE 改主题名称和加载的样式插件相关联 */
+static const QLatin1String openharmonyThemeName("openharmony");
+QStringList QOhPlatformIntegration::themeNames() const
+{
+ return QStringList{ "harmonyos", QString(openharmonyThemeName) };
+}
+
+QPlatformTheme *QOhPlatformIntegration::createPlatformTheme(const QString &name) const
+{
+ Q_UNUSED(name);
+ return new QOhPlatformTheme;
+}
+
+QPlatformFontDatabase *QOhPlatformIntegration::fontDatabase() const
+{
+ return m_harmonyFontDatabase.data();
+}
+
+#ifndef QT_NO_CLIPBOARD
+QPlatformClipboard *QOhPlatformIntegration::clipboard() const
+{
+ return m_platformClipboard.data();
+}
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+QPlatformDrag *QOhPlatformIntegration::drag() const
+{
+ return m_drag.data();
+}
+#endif
+
+#if QT_CONFIG(vulkan)
+
+QPlatformVulkanInstance *QOhPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ return new QOhPlatformVulkanInstance(instance);
+}
+
+#endif // QT_CONFIG(vulkan)
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,127 @@
+#ifndef QOHPLATFORMINTERATION_H
+#define QOHPLATFORMINTERATION_H
+
+#include <EGL/egl.h>
+#include <qpointingdevice.h> /* Qt 6 only modify */
+#include <qscopedpointer.h>
+#include <QtGui/qtguiglobal.h>
+#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformmenu.h>
+#include <qpa/qplatformservices.h>
+#include <qpa/qplatformopenglcontext.h> /* Qt 6 only modify */
+
+
+QT_BEGIN_NAMESPACE
+
+class QOhDrag;
+class QJsDisplay;
+class QDesktopWidget;
+class QPlatformAccessCtrl;
+class QPlatformColorModeCtrl;
+class QPlatformAccessibility;
+class QOhPlatformScreen;
+class QOhFileEngineHandler;
+class QOhFontDatabase;
+class QOhPlatformInputContext;
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+class QOhPlatformAccessibility;
+class QOhDisplayManager;
+#endif
+#endif
+
+class QOhPlatformIntegration : public QPlatformIntegration
+/* Qt 6 only modify */
+# if QT_CONFIG(egl)
+ , public QNativeInterface::Private::QEGLIntegration
+# endif
+{
+ friend class QOhPlatformScreen;
+
+public:
+ QOhPlatformIntegration(const QStringList ¶mList);
+ ~QOhPlatformIntegration() override;
+
+ void initialize() override;
+ Qt::KeyboardModifiers queryKeyboardModifiers() const override;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+#ifndef QT_NO_OPENGL
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
+ /* Qt 6 only modify */
+# if QT_CONFIG(egl)
+ QOpenGLContext *createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const override;
+#endif
+#endif
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QOhPlatformScreen *screen() { return m_primaryScreen.data(); }
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
+
+ bool isVirtualDesktop() { return true; }
+ QPlatformAccessCtrl *accessCtrl() const override;
+ QPlatformAbilityCtrl *abilityCtrl() const override;
+ QPlatformFontDatabase *fontDatabase() const override;
+
+#ifndef QT_NO_CLIPBOARD
+ QPlatformClipboard *clipboard() const override;
+#endif
+
+#ifndef QT_NO_DRAGANDDROP
+ virtual QPlatformDrag *drag() const override;
+#endif
+ QPlatformInputContext *inputContext() const override;
+ QPlatformNativeInterface *nativeInterface() const override;
+ QPlatformServices *services() const override;
+
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+ virtual QPlatformAccessibility *accessibility() const override;
+#endif
+#endif
+
+ QVariant styleHint(StyleHint hint) const override;
+ Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const override;
+
+ QStringList themeNames() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
+
+#if QT_CONFIG(vulkan)
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+#endif // QT_CONFIG(vulkan)
+private:
+ EGLDisplay m_eglDisplay;
+ EGLConfig m_eglConfig;
+ QScopedPointer<QOhPlatformScreen> m_primaryScreen;
+
+ QThread *m_mainThread;
+
+ static inline bool m_showPasswordEnabled = false;
+ QScopedPointer<QOhFontDatabase> m_harmonyFontDatabase;
+ QScopedPointer<QPlatformInputContext> m_platformInputContext;
+
+#ifndef QT_NO_CLIPBOARD
+ QScopedPointer<QPlatformClipboard> m_platformClipboard;
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ QScopedPointer<QOhDrag> m_drag;
+#endif
+ QScopedPointer<QPlatformNativeInterface> m_platformNativeInterface;
+ QScopedPointer<QPlatformServices> m_platformServices;
+ QScopedPointer<QOhFileEngineHandler> m_openHarmonyFileEngineHandler;
+#ifndef QT_NO_ACCESSIBILITY
+#if defined(OHOS_SDK_VERSION) && (OHOS_SDK_VERSION > 13)
+ QScopedPointer<QOhPlatformAccessibility> m_accessibility;
+#endif
+#endif
+#if OHOS_SDK_VERSION > 13
+ QScopedPointer<QOhDisplayManager> m_displayManager;
+#else
+ QJsDisplay *m_display;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif //QOHPLATFORMINTERATION_H
new file mode 100644
@@ -0,0 +1,100 @@
+#include "qohplatformmenu.h"
+#include <QDebug>
+
+QOhPlatformMenuItem::QOhPlatformMenuItem()
+ :QPlatformMenuItem(),
+ m_menu(nullptr),
+ m_isVisible(true)
+{
+
+}
+
+QOhPlatformMenu::QOhPlatformMenu()
+ :QPlatformMenu(),
+ m_isVisible(true)
+{
+
+}
+
+void QOhPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
+{
+ QOhPlatformMenuItem *ohMenuItem = qobject_cast<QOhPlatformMenuItem *>(menuItem);
+ if(!ohMenuItem){
+ return;
+ }
+
+ if(m_childs.isEmpty()){
+ m_childs.append(ohMenuItem);
+ return;
+ }
+
+ for(int i = 0; i < m_childs.size(); ++i){
+ if(m_childs[i] == before){
+ m_childs.insert(i, ohMenuItem);
+ return;
+ }
+ }
+
+ m_childs.insert(m_childs.size(), ohMenuItem);
+ Q_EMIT menuUpdated();
+}
+
+void QOhPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem)
+{
+ QOhPlatformMenuItem *item = qobject_cast<QOhPlatformMenuItem *>(menuItem);
+ if(item)
+ m_childs.removeAll(item);
+ Q_EMIT menuUpdated();
+}
+
+void QOhPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem)
+{
+ Q_UNUSED(menuItem)
+ Q_EMIT menuUpdated();
+}
+
+QPlatformMenuItem *QOhPlatformMenu::menuItemAt(int position) const
+{
+ if (position >= 0 && m_childs.size() > position) {
+ return m_childs[position];
+ }
+
+ return nullptr;
+}
+
+QPlatformMenuItem *QOhPlatformMenu::menuItemForTag(quintptr tag) const
+{
+ if(m_childs.isEmpty()){
+ return nullptr;
+ }
+
+ for(int i = 0; i < m_childs.size(); ++i){
+ if(m_childs[i]->tag() == tag){
+ return m_childs[i];
+ }
+
+ if(m_childs[i]->menu()){
+ if(QPlatformMenuItem *ret = m_childs[i]->menu()->menuItemForTag(tag)){
+ return ret;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+QPlatformMenuItem *QOhPlatformMenu::createMenuItem() const
+{
+ return new QOhPlatformMenuItem();
+}
+
+QPlatformMenu *QOhPlatformMenu::createSubMenu() const
+{
+ return new QOhPlatformMenu();
+}
+
+QList<QOhPlatformMenuItem *> QOhPlatformMenu::childItems()
+{
+ return m_childs;
+}
+
new file mode 100644
@@ -0,0 +1,74 @@
+#ifndef QOHPLATFORMMENU_H
+#define QOHPLATFORMMENU_H
+
+#include <qpa/qplatformmenu.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformMenuItem: public QPlatformMenuItem{
+ Q_OBJECT
+public:
+ QOhPlatformMenuItem();
+
+ void setText(const QString &text) override { m_text = text; }
+ void setIcon(const QIcon &icon) override { Q_UNUSED(icon)}
+ void setMenu(QPlatformMenu *menu) override { m_menu = menu; }
+ void setVisible(bool isVisible) override { m_isVisible = isVisible; }
+ void setIsSeparator(bool isSeparator) override { m_isSeparator = isSeparator; }
+ void setFont(const QFont &font) override { Q_UNUSED(font)}
+ void setRole(MenuRole role) override { Q_UNUSED(role)}
+ void setCheckable(bool checkable) override { Q_UNUSED(checkable)}
+ void setChecked(bool isChecked) override { Q_UNUSED(isChecked)}
+#ifndef QT_NO_SHORTCUT
+ void setShortcut(const QKeySequence& shortcut) override { Q_UNUSED(shortcut)}
+#endif
+ void setEnabled(bool enabled) override { Q_UNUSED(enabled)}
+ void setIconSize(int size) override { Q_UNUSED(size)}
+
+ QPlatformMenu *menu() { return m_menu; }
+ QString text() { return m_text; }
+ bool isVisible() { return m_isVisible; }
+ bool isSeparator() { return m_isSeparator; }
+
+private:
+ QString m_text;
+ QPlatformMenu *m_menu;
+ bool m_isSeparator;
+ bool m_isVisible;
+};
+
+class QOhPlatformMenu: public QPlatformMenu{
+ Q_OBJECT
+
+public:
+ QOhPlatformMenu();
+
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
+ void removeMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncSeparatorsCollapsible(bool enable) override { Q_UNUSED(enable) };
+
+ void setText(const QString &text) override { m_text = text; }
+ void setIcon(const QIcon &icon) override { Q_UNUSED(icon); }
+ void setEnabled(bool enabled) override { Q_UNUSED(enabled); }
+ void setVisible(bool visible) override { m_isVisible = visible; };
+
+ QPlatformMenuItem *menuItemAt(int position) const override;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
+ QPlatformMenuItem *createMenuItem() const override;
+ QPlatformMenu *createSubMenu() const override;
+
+ QList<QOhPlatformMenuItem *> childItems();
+
+Q_SIGNALS:
+ void menuUpdated();
+
+private:
+ QList<QOhPlatformMenuItem *> m_childs;
+ QString m_text;
+ bool m_isVisible;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMMENU_H
new file mode 100644
@@ -0,0 +1,144 @@
+#include <QtCore/qhash.h>
+
+#include "qohmain.h"
+#include "qjsability.h"
+#include "qjscontext.h"
+#include "qohwindownode.h"
+#include "qjswindowstage.h"
+#include "qohnativewindow.h"
+#include "qohplatformwindow.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatformnativeinterface.h"
+
+QT_BEGIN_NAMESPACE
+
+enum ResourceType {
+ InvalidType,
+ UIAbility,
+ NativeNode,
+ UIAbilityContext,
+ NativeXComponentType,
+ NativeJsWindowType,
+ NativeWindowStageType,
+ NativeSettings
+#if QT_CONFIG(vulkan)
+ , NativeVkSurface
+#endif
+};
+
+static ResourceType resourceType(const QByteArray &key)
+{
+ static QHash<QByteArray, ResourceType> resources = {
+ {"nativeXComponent", NativeXComponentType},
+ {"nativeWindow", NativeJsWindowType},
+ {"UIAbility", UIAbility},
+ {"NativeNode", NativeNode},
+ {"UIAbilityContext", UIAbilityContext},
+ {"nativeWindowStage", NativeWindowStageType},
+ {"nativeSettings", NativeSettings},
+#if QT_CONFIG(vulkan)
+ {"vkSurface", NativeVkSurface},
+#endif
+ };
+ if (resources.contains(key))
+ return resources.value(key);
+ return InvalidType;
+}
+
+void *QOhPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
+{
+ ResourceType type = resourceType(resource);
+ switch (type) {
+ case UIAbility:
+ {
+ auto ability = QOhNativeWindowManager::instance()->defaultAbility();
+ return ability ? std::bit_cast<void*>(ability) : nullptr;
+ }
+ case UIAbilityContext:
+ {
+ auto ability = QOhNativeWindowManager::instance()->defaultAbility();
+ return ability ? std::bit_cast<void*>(ability->context()) : nullptr;
+ }
+ case NativeSettings:
+ {
+ return std::bit_cast<void*>(QJsSettings::instance());
+ }
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+void *QOhPlatformNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
+{
+ ResourceType type = resourceType(resource);
+ void *result = nullptr;
+ QOhWindowNode *node = QOhWindowNode::fromId(window->winId());
+ switch (type) {
+ case NativeXComponentType:
+ {
+ if (node != nullptr)
+ result = node->component();
+ }
+ break;
+ case NativeNode:
+ {
+ if (nullptr != node)
+ result = node;
+ }
+ break;
+ case NativeJsWindowType:
+ {
+ if (node != nullptr) {
+ QJsObject *obj = node->ownerWindow();
+ result = obj;
+ }
+ }
+ break;
+ case NativeWindowStageType:
+ {
+ if (node != nullptr) {
+ QOhNativeWindow *window = node->ownerWindow();
+ if (window != nullptr) {
+ QJsAbility *ability = window->ability();
+ if (ability != nullptr) {
+ QJsContext *context = ability->context();
+ if (context != nullptr) {
+ QJsObject *obj = context->windowStage();
+ result = obj;
+ }
+ }
+ }
+ }
+ break;
+ }
+#if QT_CONFIG(vulkan)
+ case NativeVkSurface:
+ {
+ if (node != nullptr) {
+ result = static_cast<void*>(node->vkSurface());
+ }
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+QPlatformNativeInterface::NativeResourceForIntegrationFunction QOhPlatformNativeInterface::nativeResourceFunctionForIntegration(const QByteArray &resource)
+{
+ if (!resource.compare("isFreeWindowEnable")) {
+ return []{
+ static bool ret = false;
+ ret = QtOh::isFreeWindowEnable();
+ return std::bit_cast<void*>(&ret);
+ };
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,19 @@
+#ifndef QOHPLATFORMNATIVEINTERFACE_H
+#define QOHPLATFORMNATIVEINTERFACE_H
+
+#include <qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformNativeInterface: public QPlatformNativeInterface
+{
+ Q_OBJECT
+public:
+ void *nativeResourceForIntegration(const QByteArray &resource) override;
+ void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
+ NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMNATIVEINTERFACE_H
new file mode 100644
@@ -0,0 +1,30 @@
+#include "qohplatformoffscreensurface.h"
+
+#include <QtGui/QOffscreenSurface>
+#include <private/qeglconvenience_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhPlatformOffscreenSurface::QOhPlatformOffscreenSurface(EGLNativeWindowType surfaceTexture, EGLDisplay display,
+ const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface)
+ : QPlatformOffscreenSurface(offscreenSurface)
+ , m_format(format)
+ , m_display(display)
+ , m_surface(EGL_NO_SURFACE)
+{
+ EGLConfig config = q_configFromGLFormat(m_display, m_format, false);
+ if (config) {
+ const EGLint attributes[] = {
+ EGL_NONE
+ };
+ m_surface = eglCreateWindowSurface(m_display, config, surfaceTexture, attributes);
+ }
+}
+
+QOhPlatformOffscreenSurface::~QOhPlatformOffscreenSurface()
+{
+ eglDestroySurface(m_display, m_surface);
+}
+
+QT_END_NAMESPACE
+
new file mode 100644
@@ -0,0 +1,28 @@
+#ifndef QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H
+#define QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H
+
+#include <qpa/qplatformoffscreensurface.h>
+#include <private/qeglplatformcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+class QOffscreenSurface;
+class QOhPlatformOffscreenSurface : public QPlatformOffscreenSurface
+{
+public:
+ QOhPlatformOffscreenSurface(EGLNativeWindowType surfaceTexture, EGLDisplay display, const QSurfaceFormat &format,
+ QOffscreenSurface *offscreenSurface);
+ ~QOhPlatformOffscreenSurface();
+
+ QSurfaceFormat format() const override { return m_format; }
+ bool isValid() const override { return m_surface != EGL_NO_SURFACE; }
+
+ EGLSurface surface() const { return m_surface; }
+private:
+ QSurfaceFormat m_format;
+ EGLDisplay m_display;
+ EGLSurface m_surface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYPLATFORMOFFSCREENSURFACETEXTURE_H
new file mode 100644
@@ -0,0 +1,32 @@
+#include "qohplatformopenglcontext.h"
+#include "qohplatformopenglwindow.h"
+#include <QDebug>
+#include <QSurface>
+#include <QtGui/QOffscreenSurface>
+#include <QtGui/private/qopenglcontext_p.h>
+#include <private/qeglpbuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QOhPlatformOpenGLContext::QOhPlatformOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config)
+ :QEGLPlatformContext(format, share, display, config, Flags())
+{
+}
+
+EGLSurface QOhPlatformOpenGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
+{
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ if (auto *w = dynamic_cast<QOhPlatformOpenGLWindow *>(surface)) {
+ EGLSurface surface = w->eglSurface(eglDisplay(), eglConfig());
+ return surface;
+ }
+ return nullptr;
+ } else {
+ if (auto *platformOffscreenSurface = dynamic_cast<QPlatformOffscreenSurface *>(surface))
+ return platformOffscreenSurface->surface();
+ else
+ return static_cast<QEGLPbuffer *>(surface)->pbuffer();
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,20 @@
+#ifndef QOPENHARMONYPLATFORMOPENGLCONTEXT_H
+#define QOPENHARMONYPLATFORMOPENGLCONTEXT_H
+
+#include <private/qeglplatformcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformOpenGLContext : public QEGLPlatformContext
+{
+public:
+ using QEGLPlatformContext::QEGLPlatformContext; /* Qt 6 only modify */
+ QOhPlatformOpenGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config);
+private:
+ EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYPLATFORMOPENGLCONTEXT_H
new file mode 100644
@@ -0,0 +1,920 @@
+#include "qohplatformopenglwindow.h"
+#include "qohxcomponent.h"
+#include "qohnativewindow.h"
+#include "qohplatformscreen.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatformbackingstore.h"
+
+#include "qohmain.h"
+#include "qohutility.h"
+#include "qjsability.h"
+#include "qjscontext.h"
+#include "qohwindowcontext.h"
+#include "qohwindownode.h"
+#include "qohplatformcursor.h"
+#include "qohsystemtrayicon.h"
+#include "qohdisplaymanager.h"
+#include "qohplatforminputcontext.h"
+
+#include <QDebug>
+#include <QTimer>
+#include <QColor>
+#include <QPalette>
+#include <QPainter>
+#include <QStaticText>
+#include <QSurfaceFormat>
+#include <QGuiApplication>
+#include <qpa/qplatformtheme.h>
+#include <qpa/qplatformscreen.h>
+#include <qpa/qplatformwindow_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qwindow_p.h>
+#include <QtGui/private/qpainter_p.h>
+#include <private/qguiapplication_p.h>
+#include <private/qeglconvenience_p.h> /* Qt6 only modify */
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qwindowsysteminterface_p.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#if OHOS_SDK_VERSION > 13
+#include "qohaccessibilityprovider.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+enum {
+ defaultWindowWidth = 800,
+ defaultWindowHeight = 600
+};
+
+QOhNativeWindow *QOhPlatformOpenGLWindow::m_mouseGrab = nullptr;
+
+QOhPlatformOpenGLWindow::QOhPlatformOpenGLWindow(QWindow *window)
+ :QOhPlatformWindow(window)
+ , m_nativeWindow(nullptr)
+{
+ QRect rect = window->geometry();
+ m_rect = QHighDpi::toNativeLocalPosition(rect, window);
+ const auto *wp = qt_window_private(const_cast<QWindow*>(window));
+ auto *platformScreen = QOhDisplayManager::startScreen();
+ auto nativeTopLeft = platformScreen->geometry().topLeft();
+ if (window->isTopLevel() && wp->positionAutomatic && window->type() != Qt::Popup) {
+ m_rect = m_rect.translated(nativeTopLeft);
+ }
+ // 根据坐标查找屏幕,如果没有查到,使用startScreen。解决只有一个屏幕,主屏左上角坐标不为(0,0)的情况
+ if (window->isTopLevel()) {
+ auto targetPlatformScreen = QOhDisplayManager::instance()->screenAt(m_rect.center());
+ if (targetPlatformScreen == nullptr) {
+ m_rect = m_rect.translated(nativeTopLeft);
+ }
+ }
+ static bool firstMainWindow = true;
+ if (firstMainWindow) {
+ firstMainWindow = false;
+ /* FIXME wanghao 全局光标参与启动计算,临时设置一个启动时全局光标 */
+ QCursor::setPos(platformScreen->geometry().center());
+ }
+ if (window->type() != Qt::Desktop) {
+ QOhPlatformScreen *screen = this->platformScreen();
+ auto targetPlatformScreen = QOhDisplayManager::instance()->screenAt(m_rect.center());
+ if (targetPlatformScreen && screen != targetPlatformScreen) {
+ QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window, targetPlatformScreen->screen());
+ }
+ }
+}
+
+QOhPlatformOpenGLWindow::~QOhPlatformOpenGLWindow()
+{
+ if (!m_nativeWindow.isNull()) {
+ if (m_mouseGrab == m_nativeWindow.data())
+ m_mouseGrab = nullptr;
+ qNativeWindowManager->destroyWindow(m_nativeWindow.take());
+ }
+}
+
+// 当设置了type flags时需要调整window
+static inline void fixTopLevelWindowFlags(QWindow *w)
+{
+ if (!w->isTopLevel())
+ return;
+ Qt::WindowFlags flags = w->flags();
+ flags &= ~Qt::WindowFullscreenButtonHint;
+
+ /* 添加悬浮窗自带的属性 */
+ if (flags.testFlag(Qt::Floating))
+ flags |= Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
+
+ switch (flags) {
+ case Qt::Window:
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint
+ |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
+ break;
+ case Qt::Dialog:
+ case Qt::Tool:
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+ break;
+ default:
+ break;
+ }
+ if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
+ flags |= Qt::FramelessWindowHint;
+
+ w->setFlags(flags);
+}
+
+void QOhPlatformOpenGLWindow::initialize()
+{
+ QOhPlatformWindow::initialize();
+ QPlatformScreen *qScreen = screen();
+ QWindow *qWindow = window();
+ fixTopLevelWindowFlags(qWindow);
+ QRect rect;
+ if (isGeometryControlledBySystem()) {
+ rect = screen()->availableGeometry();
+ } else {
+ QRect windowGeometry = m_rect;
+ QScreen *resultScreen = nullptr;
+ rect = initialGeometry(qWindow, windowGeometry, defaultWindowWidth, defaultWindowHeight,
+ (const QScreen **)&resultScreen);
+
+ if (qWindow->type() != Qt::Desktop) {
+ if (resultScreen && qScreen != resultScreen->handle()) {
+ // Trigger geometry change (unless it has a special state in which case setWindowState()
+ // will send the message) and screen change signals of QWindow.
+ QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(qWindow, resultScreen);
+ }
+ }
+
+ // 设置到屏幕中间, initialGeometry中,当窗口的大小大于屏幕大小的8/9时就不会移到中间
+ const auto *wp = qt_window_private(const_cast<QWindow*>(qWindow));
+ const bool position = wp->positionAutomatic && (qWindow->type() != Qt::Popup) && qWindow->isTopLevel();
+ if (resultScreen !=nullptr && position) {
+ const QRect availableGeometry = resultScreen->handle()->availableGeometry();
+ const QWindow *tp = qWindow->transientParent();
+ if (rect.width() <= availableGeometry.width()
+ && rect.height() <= availableGeometry.height()) {
+ if (tp) {
+ // A transient window should be centered w.r.t. its transient parent.
+ rect.moveCenter(tp->handle()->geometry().center());
+ } else {
+ rect.moveCenter(availableGeometry.center());
+ }
+ }
+ }
+ }
+ QRect oldRect = m_rect;
+ m_rect = rect;
+ createNativeWindowNode();
+ m_normalRect = rect;
+ const Qt::WindowState state = qWindow->windowState();
+ if (qWindow->type() != Qt::Desktop && state != Qt::WindowMaximized && state != Qt::WindowFullScreen && oldRect != m_rect)
+ QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(qWindow, m_rect);
+
+#if OHOS_SDK_VERSION > 13
+ bool enable = QtOh::isEnvironmentVariableIsTrue("QT_ENABLE_ACCESSIBILITY", false);
+ if (enable)
+ QOhAccessibilityProvider::registerForWindow(window());
+#endif
+ updateNodeRenderFit();
+}
+
+QOhPlatformOpenGLWindow::Kind QOhPlatformOpenGLWindow::windowKind() const
+{
+ auto qWindow = window();
+ auto parent = qWindow->parent();
+ auto transientParent = qWindow->transientParent();
+ auto windowType = qWindow->type();
+ if (windowType == Qt::SplashScreen)
+ return Kind::SplashWindow;
+
+ static QSet<Qt::WindowType> subWindowTypes = {
+ Qt::ToolTip,
+ Qt::Popup,
+ Qt::Floating
+ };
+ bool autoSubWindow = subWindowTypes.contains(windowType);
+
+ Kind initialWindowKind;
+ if (parent != nullptr) {
+ initialWindowKind = Kind::NativeNode;
+ } else if (transientParent != nullptr) {
+ initialWindowKind = Kind::SubWindow;
+ } else if (autoSubWindow || (isModal() && QtOh::apiVersion() < 14) ) {
+ if (m_volatileWindowKind != Kind::InValid)
+ return m_volatileWindowKind;
+
+ auto focusWindow = qGuiApp->focusWindow();
+ // QWidget::show没有激活窗口, 需要调用QWidget::activateWindow、找到focusWindow才是最新显示的窗口
+ QWindow *topWindow = nullptr;
+ QWindowList list = qApp->topLevelWindows();
+ for(int i = list.size() - 1; i >= 0; i--){
+ if (qWindow != list[i] && list[i]->isVisible()){
+ topWindow = list[i];
+ }
+ }
+
+ auto p = (focusWindow && focusWindow != qWindow) ? focusWindow : topWindow;
+ /* FIXME tooltip创建为鸿蒙主窗口
+ * p != qWindow造成焦点在其他非Qt窗口时,
+ * Qt窗口的tooltip创建为主窗口
+ */
+ if (p != nullptr/* && p != qWindow*/) {
+ /* 鸿蒙系统下,如果父窗口隐藏掉, 子窗口无法显示 */
+ // !p->isTopLevel() ||
+ while (p && !p->isVisible() && (p->type() == Qt::Popup || p->type() == Qt::ToolTip)) {
+ p = p->parent(QWindow::ExcludeTransients);
+ }
+ /* 如果没有显示的窗口,只能创建主窗口 */
+ if (p == nullptr) {
+ initialWindowKind = Kind::MainWindow;
+ } else {
+ qWindow->setTransientParent(p);
+ initialWindowKind = Kind::SubWindow;
+ }
+ } else {
+ initialWindowKind = Kind::MainWindow;
+ }
+ m_volatileWindowKind = initialWindowKind;
+ } else {
+ initialWindowKind = Kind::MainWindow;
+ }
+ return initialWindowKind;
+}
+
+void QOhPlatformOpenGLWindow::setMask(const QRegion ®ion)
+{
+ if (m_nativeWindow.isNull()) {
+ m_initMask = region;
+ return;
+ }
+
+ QRect rect = m_nativeWindow->geometry();
+ m_nativeWindow->setMask(region, rect.isEmpty() ? m_normalRect : rect);
+}
+
+void QOhPlatformOpenGLWindow::setGeometry(const QRect &rect)
+{
+ m_rect = rect;
+ QOhPlatformWindow::setGeometry(rect);
+ if (isGeometryControlledBySystem()) {
+ return;
+ }
+
+ if (m_nativeWindow.isNull()) {
+ m_normalRect = m_rect;
+ return;
+ }
+
+ auto kind = windowKind();
+ // 设置nativewindow的尺寸
+ if (kind != QOhPlatformOpenGLWindow::Kind::SplashWindow) {
+ m_nativeWindow->setGeometry(rect);
+ }
+}
+
+QRect QOhPlatformOpenGLWindow::geometry() const
+{
+ return m_rect;
+}
+
+EGLSurface QOhPlatformOpenGLWindow::eglSurface(EGLDisplay display, EGLConfig config)
+{
+ if (m_eglSurface == EGL_NO_SURFACE) {
+ checkNativeSurface(display, config);
+ }
+ return m_eglSurface;
+}
+
+bool QOhPlatformOpenGLWindow::checkNativeSurface(EGLDisplay display, EGLConfig config)
+{
+ if (nullptr == m_node || !window())
+ return false;
+ m_eglSurface = m_node->eglSurface(display, config);
+ m_format = q_glFormatFromConfig(display, config, window()->requestedFormat());
+ return true;
+}
+
+void QOhPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState state)
+{
+ QOhPlatformWindow::applicationStateChanged(state);
+ if (state <= Qt::ApplicationHidden) {
+ }
+}
+
+void QOhPlatformOpenGLWindow::propagateSizeHints()
+{
+ if (m_nativeWindow.isNull())
+ return;
+ QOhNativeWindow::WindowLimits limits(windowMinimumSize(), windowMaximumSize());
+ m_nativeWindow->setWindowLimits(limits, QtOh::Utility::type() == QtOh::Utility::PC);
+}
+
+QSurfaceFormat QOhPlatformOpenGLWindow::format() const
+{
+ if (m_eglSurface == 0)
+ return window()->requestedFormat();
+ else
+ return m_format;
+}
+
+void QOhPlatformOpenGLWindow::setWindowFlags(Qt::WindowFlags flags)
+{
+ if (m_nativeWindow.isNull())
+ return;
+
+ bool isFrameLess = flags.testFlag(Qt::FramelessWindowHint);
+
+ if (QtOh::isSupportFreeWindow()) {
+ if (m_nativeWindow->isTopWindow()) {
+ m_nativeWindow->setWindowTitleButtonVisible(flags.testFlag(Qt::WindowMaximizeButtonHint),
+ flags.testFlag(Qt::WindowMinimizeButtonHint),
+ flags.testFlag(Qt::WindowCloseButtonHint));
+ m_nativeWindow->setWindowTopmost(flags.testFlag(Qt::WindowStaysOnTopHint));
+ }
+ m_nativeWindow->setWindowTitleMoveEnabled(!isFrameLess);
+ }
+
+ /* 窗口背景透明初始化,仅支持透明背景色及qt默认背景色,其它背景色应使用OhExtras模块接口设置 */
+ QWindow *w = window();
+ bool isTransparent = w->format().hasAlpha() && flags.testFlag(Qt::FramelessWindowHint);
+ if (isTransparent) {
+ m_nativeWindow->setWindowBackgroundColor(Qt::transparent);
+ } else {
+ QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+ const QPalette *pal = theme->palette();
+ m_nativeWindow->setWindowBackgroundColor(pal->color(QPalette::Window));
+ }
+
+ /* 设置窗口是否鼠标事件(鼠标透传) */
+ bool isTouchable = !flags.testFlag(Qt::WindowTransparentForInput);
+ m_nativeWindow->setWindowTouchable(isTouchable);
+}
+
+void QOhPlatformOpenGLWindow::setParent(const QPlatformWindow *window)
+{
+ if (window && !m_nativeWindow.isNull()) {
+ destroyNativeWindow();
+ }
+
+ QOhPlatformWindow::setParent(window);
+}
+
+void QOhPlatformOpenGLWindow::requestActivateWindow()
+{
+ if (!m_nativeWindow.isNull() && m_nativeWindow->isVisible()) {
+ m_nativeWindow->requestFocus();
+ /*置前*/
+ m_nativeWindow->raise();
+ } else {
+ m_requestActivate = true;
+ }
+ // 如果没有显示,设置不生效,在setVisible中重新设置
+ if (m_node && m_node->isVisible())
+ m_node->setNodeFocus(true);
+ else
+ m_focus = true;
+}
+
+void QOhPlatformOpenGLWindow::setWindowState(Qt::WindowStates state)
+{
+ if (state == m_windowState)
+ return;
+
+ auto newState = state;
+ if ((QtOh::isTabletDevice() || QtOh::isPhoneDevice()) &&
+ !QtOh::isFreeWindowEnable()) {
+ newState = ((window()->flags() & Qt::Window) && (window()->transientParent() || window()->parent())) ? Qt::WindowNoState : state;
+ }
+ setWindowState_sys(newState);
+ m_windowState = newState;
+ QRect rect = m_rect;
+ auto *screen = platformScreen();
+ if (!screen)
+ screen = dynamic_cast<QOhPlatformScreen *>(QGuiApplication::primaryScreen()->handle());
+ if (m_windowState.testFlag(Qt::WindowMaximized)) {
+ m_rect = screen->availableGeometry();
+ }
+ if (m_windowState.testFlag(Qt::WindowFullScreen)) {
+ m_rect = screen->geometry();
+ }
+ if (rect != m_rect) {
+ QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(window(), m_rect);
+ }
+}
+
+void QOhPlatformOpenGLWindow::createNativeWindow()
+{
+ QWindow *window = this->window();
+ QRect initRect = QOhNativeWindow::adjustRect(window, m_normalRect, frameMargins());
+ qCWarning(openharmonyQPA) << "create open harmony window for" << this << "windowFlag:" << window->flags() << initRect;
+ initRect = initRect.translated(-platformScreen()->geometry().topLeft());
+ m_nativeWindow.reset(qNativeWindowManager->createNativeWindow(this, initRect));
+ if (m_nativeWindow.isNull()) {
+ deleteNativeWindowNode();
+ return;
+ }
+ qCWarning(openharmonyQPA)
+ << Q_FUNC_INFO << m_nativeWindow.data()
+ << "m_normalRect:" << initRect
+ << "native window name: " << m_nativeWindow->name();
+ if (m_nativeWindow->nativeWindowType() == QOhNativeWindow::EXTENSION_WINDOW) {
+ m_rect = m_nativeWindow->geometry();
+ }
+
+ bool active = !showWithoutActive();
+ if (active) {
+ m_focus = true;
+ }
+
+ applyCursor();
+
+ m_nativeWindow->setWindowTitleVisible(this->hasFrame());
+ setWindowFlags(window->flags());
+ if (!m_initMask.isEmpty()) {
+ setMask(m_initMask);
+ }
+ propagateSizeHints();
+ m_nativeWindow->setWindowTitle(windowTitle());
+
+ if (isGeometryControlledBySystem()) {
+ // 窗口未显示,无法获取到正确的值,使用屏幕的大小
+// QRect rect = m_nativeWindow->geometry();
+ QRect rect = screen()->availableGeometry();
+ m_nativeWindow->reportWindowGeometry(rect);
+ } else {
+ QTimer::singleShot(10, window, [this]{
+ if (m_nativeWindow.isNull())
+ return ;
+ m_nativeWindow->reportWindowGeometry();
+ });
+
+ if (m_windowState == Qt::WindowNoState) {
+ m_nativeWindow->setGeometry(m_normalRect);
+ }
+ }
+}
+
+void QOhPlatformOpenGLWindow::setVisible(bool visible)
+{
+ m_isExposed = visible;
+ Kind kind = windowKind();
+ if (kind != Kind::NativeNode && m_nativeWindow.isNull() && visible) {
+ createNativeWindow();
+ }
+
+ if (visible) {
+ showNativeNode();
+ showNativeWindow();
+ fireFullExpose(true);
+ } else {
+ if (m_nativeWindow.isNull()) {
+ hideNativeNode();
+ } else {
+ hideNativeWindow();
+ }
+ if (QOhWindowContext::mouseGrabberWindow() == window())
+ QOhWindowContext::setMouseGrabberWindow(nullptr);
+
+ /* popup窗口不会获取焦点,显示和隐藏不会触发焦点转移,在此处更新输入法状态 */
+ if (window()->type() == Qt::Popup)
+ QOhPlatformInputContext::ohInputContext()->updateInputEnabled();
+ fireExpose(QRegion());
+ QOhPlatformBackingStore::releaseVsync(window());
+ }
+ m_windowIsHidden = !visible;
+}
+
+QOhNativeWindow *QOhPlatformOpenGLWindow::nativeWindow() const
+{
+ return m_nativeWindow.data();
+}
+
+QMargins QOhPlatformOpenGLWindow::frameMargins() const
+{
+ if (m_nativeWindow.isNull()) {
+ QMargins m;
+ if (!hasFrame() || m_windowState.testFlag(Qt::WindowFullScreen))
+ return m;
+ static int titleHeight = 37;
+ m = QMargins(0, titleHeight * QtOh::densityPixels(screen()), 0, 0);
+ return m;
+ }
+
+ return m_nativeWindow->frameMargins();
+}
+
+bool QOhPlatformOpenGLWindow::setWindowState_sys(Qt::WindowStates state)
+{
+ if (m_nativeWindow.isNull())
+ return false;
+
+ Kind kind = windowKind();
+ if (!isGeometryControlledBySystem() && kind == Kind::MainWindow)
+ m_nativeWindow->setWindowModal(isModal());
+
+ //NOTE:isVisible()接口在窗口最小化时返回false
+ const QtOh::WindowStatusType windowStatus = m_nativeWindow->windowStatus();
+ bool visible = m_nativeWindow->isVisible() || windowStatus == QtOh::WindowStatusType::MINIMIZE;
+ auto oldState = m_windowState;
+
+ auto setNativeWindowState = [this](Qt::WindowStates state) {
+ if (state & Qt::WindowFullScreen) {
+ m_nativeWindow->showFullScreen();
+ } else if (state & Qt::WindowMaximized) {
+ m_nativeWindow->showMaximized();
+ } else {
+ m_nativeWindow->showNormal();
+ }
+ };
+
+ const auto stateChange = oldState ^ state;
+ if (stateChange & Qt::WindowFullScreen) {
+ setNativeWindowState(state);
+ } else if ((oldState & Qt::WindowMaximized) != (state & Qt::WindowMaximized)) {
+ if (visible && !(state & Qt::WindowMinimized)) {
+ if (state & Qt::WindowMaximized)
+ m_nativeWindow->showMaximized();
+ else
+ m_nativeWindow->showNormal();
+ } else if (visible && (oldState & state & Qt::WindowMinimized)) {
+
+ }
+ } else if (stateChange == Qt::WindowNoState) {
+ setNativeWindowState(state);
+ }
+ if (stateChange & Qt::WindowMinimized) {
+ if (visible) {
+ if (state & Qt::WindowMinimized) {
+ fireExpose();
+ m_minimized = true;
+ m_nativeWindow->showMinimized();
+ } else {
+ setNativeWindowState(state);
+ }
+ }
+ }
+ return true;
+}
+
+void QOhPlatformOpenGLWindow::raise()
+{
+ QOhPlatformWindow::raise();
+ if (m_nativeWindow.isNull()) {
+ return;
+ }
+ m_nativeWindow->raise();
+}
+
+bool QOhPlatformOpenGLWindow::canApplyNewCursor(const QWindow *w)
+{
+ const QWindow *underMouse = QOhWindowContext::underMouseWindow();
+ if (underMouse == w)
+ return true;
+ for (const QWindow *p = underMouse; p ; p = p->parent()) {
+ if (p == w)
+ return true;
+ const QOhPlatformOpenGLWindow *platformWindow = platformWindowOfWindow(p);
+ if (platformWindow && platformWindow->m_cursor != nullptr)
+ return false;
+ }
+ return false;
+}
+
+void QOhPlatformOpenGLWindow::setCursor(QCursorInfo *cursor)
+{
+#ifndef QT_NO_CURSOR
+ bool changed = (cursor != m_cursor);
+ if (changed) {
+ m_cursor = cursor;
+ QWindow *w = window();
+ const bool apply = canApplyNewCursor(w);
+ //qInfo(openharmonyQPA) << apply <<(m_cursor ? m_cursor->shape : -1) << w;
+ if (apply)
+ applyCursor();
+ }
+#endif
+}
+
+bool QOhPlatformOpenGLWindow::startSystemMove()
+{
+ if (m_nativeWindow.isNull())
+ return false;
+ return m_nativeWindow->startSystemMove();
+}
+
+void QOhPlatformOpenGLWindow::setWindowTitle(const QString &title)
+{
+ if (m_nativeWindow.isNull())
+ return;
+ return m_nativeWindow->setWindowTitle(title);
+}
+
+bool QOhPlatformOpenGLWindow::isGeometryControlledBySystem() const
+{
+ return (QtOh::Utility::type() != QtOh::Utility::PC) &&
+ ((window()->flags() & Qt::WindowType_Mask) == Qt::Window || Kind::MainWindow == windowKind()) &&
+ !QtOh::isFreeWindowEnable() && !window()->transientParent();
+}
+
+void QOhPlatformOpenGLWindow::setNativeGeometry(const QRect &rect)
+{
+ m_rect = rect;
+}
+
+int32_t QOhPlatformOpenGLWindow::nativeWindowId() const
+{
+ if (!m_nativeWindow.isNull())
+ return m_nativeWindow->id();
+ return 0;
+}
+
+QOhPlatformOpenGLWindow *QOhPlatformOpenGLWindow::platformWindowOfWindow(const QWindow *w)
+{
+ if (!w || !w->handle())
+ return nullptr;
+
+ const Qt::WindowType type = w->type();
+ if (type == Qt::Desktop || w->handle()->isForeignWindow())
+ return nullptr;
+
+ return static_cast<QOhPlatformOpenGLWindow *>(w->handle());
+}
+
+void QOhPlatformOpenGLWindow::applyCursor()
+{
+#ifndef QT_NO_CURSOR
+ const QOhNativeWindow *nativeWindow = this->nativeWindow();
+ QWindow *w = window();
+ while(!nativeWindow && (w = w->parent())) {
+ QOhPlatformOpenGLWindow *pw = static_cast<QOhPlatformOpenGLWindow *>(w->handle());
+ if (!pw)
+ break;
+ nativeWindow = pw->nativeWindow();
+ }
+ if (nativeWindow == nullptr)
+ return;
+ int wid = nativeWindow->id();
+ if (QOhPlatformCursor::hasOverrideCursor()) {
+ if (isTopLevelWindow())
+ QOhPlatformCursor::enforceOverrideCursor(wid);
+ return;
+ }
+
+ if (m_cursor == nullptr) {
+ if (const QWindow *p = window()->parent()) {
+ if (QOhPlatformOpenGLWindow *platformWindow = platformWindowOfWindow(p))
+ platformWindow->applyCursor();
+ } else {
+ QOhPlatformCursor::setDefaultCursor(wid);
+ }
+ } else {
+ QOhPlatformCursor::setCursor(wid, m_cursor);
+ }
+#endif
+}
+
+void QOhPlatformOpenGLWindow::showNativeWindow()
+{
+ if (m_nativeWindow.isNull())
+ return;
+
+ if (showWithoutActive()) {
+ m_nativeWindow->setWindowFocusable(false);
+ }
+
+ Kind kind = windowKind();
+ bool hideEnable = (kind == Kind::MainWindow && QOhSystemTrayIcon::hasTrayIcon() && QtOh::apiVersion() > 13);
+ if (hideEnable) {
+ QJsAbility *ability = m_nativeWindow->ability();
+ if (ability && ability->context()) {
+ m_nativeWindow->setShowSysFlag();
+ ability->context()->showAbility();
+ }
+ bool active = !showWithoutActive();
+ if (active) {
+ m_node->setNodeFocus(true);
+ }
+ }
+
+ setWindowState_sys(m_windowState);
+
+ if (m_requestActivate) {
+ m_nativeWindow->requestFocus();
+ m_requestActivate = std::nullopt;
+ }
+ m_nativeWindow->raiseNativeWindow();
+
+ // 直接执行时,窗口可能还没有执行显示操作,导致显示时会获取焦点
+ if (showWithoutActive() && canSetFocus()) {
+ QTimer::singleShot(50, window(), [this]{
+ if (m_nativeWindow.isNull())
+ return;
+ m_nativeWindow->setWindowFocusable(true);
+ });
+ };
+}
+
+void QOhPlatformOpenGLWindow::hideNativeWindow()
+{
+ if (m_nativeWindow.isNull())
+ return;
+
+ Kind kind = windowKind();
+ bool hideed = false;
+ if (Kind::MainWindow == kind && QtOh::apiVersion() > 13) {
+ QJsAbility *ability = m_nativeWindow->ability();
+ if(ability && ability->context())
+ hideed = ability->context()->hideAbility();
+ }
+
+ if (hideed)
+ return;
+
+ if (kind == Kind::MainWindow || kind == Kind::SplashWindow) {
+ destroyNativeWindow();
+ } else if (kind == Kind::SubWindow) {
+ m_nativeWindow->setVisible(false);
+ if (m_mouseGrab == m_nativeWindow.data()) {
+ m_mouseGrab->setMouseGrabEnabled(false);
+ m_mouseGrab = nullptr;
+ }
+ updateUnderMouseWindow();
+ }
+}
+
+void QOhPlatformOpenGLWindow::destroyNativeWindow()
+{
+ /* 直接销毁,setParent()设置父widget时,原js窗口应销毁 */
+ // 记录隐藏之前的大小,下次显示的时候重新是重新创建js窗口
+ // 在次记录下次显示的设置
+ // 从m_nativeWindow获取可能最新的size还未更新(resize后立即hide)
+ m_normalRect = m_nativeWindow->normalGeometry();
+ m_normalRect = m_normalRect.translated(platformScreen()->geometry().topLeft());
+ if (m_mouseGrab == m_nativeWindow.data()) {
+ m_mouseGrab->setMouseGrabEnabled(false);
+ m_mouseGrab = nullptr;
+ }
+ m_node->detachRootComponent(true);
+ m_node->setOwnerWindow(nullptr);
+ qNativeWindowManager->destroyWindow(m_nativeWindow.take());
+ updateUnderMouseWindow();
+}
+
+void QOhPlatformOpenGLWindow::updateNodeRenderFit()
+{
+ if (m_node && window()) {
+ auto var = window()->property(QOpenHarmonyWindow::NativeNodeFitIdentifier); /* Qt6 only modify */
+ if (!var.isNull())
+ m_node->setRenderFit(ArkUI_RenderFit(var.toInt()));
+ }
+}
+
+void QOhPlatformOpenGLWindow::createNativeWindowNode(WId nativeHandle)
+{
+ QOhPlatformWindow::createNativeWindowNode(nativeHandle);
+ QWindow *qWindow = window();
+ auto node = nativeWindowNode();
+ node->createNode(QRectF(m_rect), !showWithoutActive());
+ updateXComponentBackground();
+ qCInfo(openharmonyQPA) << Q_FUNC_INFO << this
+ << "rect: " << m_rect
+ << "parentWindow:" << parent()
+ << "qWindow transientParent:" << qWindow->transientParent()
+ << "windowFlags: " << qWindow->flags();
+}
+
+void QOhPlatformOpenGLWindow::updateXComponentBackground()
+{
+ QWindow *qWindow = window();
+ if (!qWindow)
+ return;
+ Qt::WindowFlags flags = qWindow->flags();
+ bool isTransparent = qWindow->format().hasAlpha() && flags.testFlag(Qt::FramelessWindowHint);
+
+ auto *node = nativeWindowNode();
+ /*
+ * NOTE quickwindow窗口不能设置XComponent背景色
+ * 设置后可能导致渲染异常
+ */
+ if (QSurface::OpenGLSurface != qWindow->surfaceType()) {
+ QVariant var = qWindow->property("_q_background");
+ QBrush brush = var.value<QBrush>();
+ QColor bgColor;
+ if (Qt::NoBrush == brush.style()) {
+ bgColor = isTransparent ? Qt::transparent : Qt::white;
+ } else {
+ bgColor = qbrush_color(brush);
+ }
+
+ node->setBackgroundColor(bgColor);
+ } else {
+ node->setBackgroundColor(Qt::transparent);
+ }
+}
+
+void QOhPlatformOpenGLWindow::updateUnderMouseWindow()
+{
+ static int s_updateCount = 0;
+ ++s_updateCount;
+
+ QTimer::singleShot(30, nullptr, [] {
+ if (--s_updateCount != 0)
+ return;
+
+ /* 等待窗口关闭的leave事件触发后,更新当前鼠标下的窗口触发enter事件 */
+ QPoint globalPos, localPos;
+ globalPos = QCursor::pos();
+
+ QWindow *qWindow = nullptr;
+ if (QScreen *windowScreen = QGuiApplication::screenAt(globalPos)) {
+ const QPoint devicePosition = QHighDpi::toNativePixels(globalPos, windowScreen);
+ globalPos = devicePosition;
+ QOhPlatformScreen *ps = dynamic_cast<QOhPlatformScreen *>(windowScreen->handle());
+ qWindow = QOhWindowContext::getWindowByCoordinate(ps, devicePosition);
+ }
+
+ if (!qWindow)
+ return;
+
+ localPos = qWindow->handle()->mapFromGlobal(globalPos);
+ QOhWindowContext::setUnderMouseWindow(qWindow, localPos, globalPos);
+ });
+}
+
+bool QOhPlatformOpenGLWindow::windowEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *propertyEvent = static_cast<QDynamicPropertyChangeEvent*>(event);
+ if (propertyEvent->propertyName() == "_q_background") {
+ updateXComponentBackground();
+ } else if (propertyEvent->propertyName() == QOpenHarmonyWindow::NativeNodeFitIdentifier) { /* Qt6 only modify */
+ updateNodeRenderFit();
+ }
+ }
+ return QOhPlatformWindow::windowEvent(event);
+}
+
+void QOhPlatformOpenGLWindow::showNativeNode()
+{
+ QOhPlatformWindow::showNativeNode();
+ if (m_node != nullptr) {
+ if (m_focus) {
+ m_node->setNodeFocus(m_focus.value());
+ m_focus = std::nullopt;
+ }
+ }
+}
+
+void QOhPlatformOpenGLWindow::handleWindowStateChange(Qt::WindowStates states)
+{
+ /*
+ * 屏蔽主动改变窗口状态的系统回调,比如STATUS_MINIMIZE
+ *下面三种情况都会收到STATUS_MINIMIZE状态回调
+ *1、QWidget::hide接口(底层使用js的minimize()接口),不更新state
+ *2、QWidget::showMinimized由qt上层更新state
+ *3、点击系统的最小化按钮,更新state为Qt::WindowMinimized
+ */
+ // hide接口回调屏蔽
+ if (m_windowIsHidden && states.testFlag(Qt::WindowMinimized))
+ states = states ^ Qt::WindowMinimized;
+
+ Qt::WindowStates newState = Qt::WindowNoState;
+
+ if (!m_nativeWindow.isNull()) {
+ m_minimized = m_nativeWindow->windowStatus() == QtOh::WindowStatusType::MINIMIZE;
+ }
+ if (m_minimized)
+ newState = Qt::WindowMinimized;
+
+ if (states.testFlag(Qt::WindowFullScreen))
+ newState |= Qt::WindowFullScreen;
+ if (states.testFlag(Qt::WindowMaximized))
+ newState |= Qt::WindowMaximized;
+ QOhPlatformWindow::handleWindowStateChange(newState);
+}
+
+void QOhPlatformOpenGLWindow::lower()
+{
+ QOhPlatformWindow::lower();
+ if (m_nativeWindow.isNull()) {
+ return;
+ }
+ m_nativeWindow->lower();
+}
+
+bool QOhPlatformOpenGLWindow::setMouseGrabEnabled(bool grab)
+{
+ if (m_nativeWindow.isNull()) {
+ return false;
+ }
+ if (grab && m_mouseGrab == m_nativeWindow.data())
+ return true;
+ if (grab && m_mouseGrab != nullptr && m_mouseGrab != m_nativeWindow.data())
+ m_mouseGrab->setMouseGrabEnabled(false);
+ bool result = m_nativeWindow->setMouseGrabEnabled(grab);
+ if (result) {
+ m_mouseGrab = grab ? m_nativeWindow.data() : nullptr;
+ QOhPlatformWindow::setMouseGrabEnabled(grab);
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,99 @@
+#ifndef QOHPLATFORMOPENGLWINDOW_H
+#define QOHPLATFORMOPENGLWINDOW_H
+
+#include <QHash>
+#include <EGL/egl.h>
+#include <QPointer>
+#include <QMutex>
+#include <optional>
+#include <QWaitCondition>
+#include "qohplatformwindow.h"
+#include "qohnativewindow.h"
+
+QT_BEGIN_NAMESPACE
+class QOhWindowNode;
+struct OH_NativeXComponent;
+
+class QOhPlatformOpenGLWindow : public QOhPlatformWindow
+{
+public:
+ enum class Kind
+ {
+ MainWindow,
+ SubWindow,
+ NativeNode,// child widget, not a independent window
+ SplashWindow,
+ InValid = 0xFFU
+ };
+ explicit QOhPlatformOpenGLWindow(QWindow *window);
+ ~QOhPlatformOpenGLWindow() override;
+
+ void initialize() override;
+ Kind windowKind() const;
+
+ void setMask(const QRegion ®ion) override;
+ void setGeometry(const QRect &rect) override;
+ QRect geometry() const override;
+
+ EGLSurface eglSurface(EGLDisplay display, EGLConfig config);
+ QSurfaceFormat format() const override;
+ void setWindowFlags(Qt::WindowFlags flags) override;
+ void setParent(const QPlatformWindow *window) override;
+ void requestActivateWindow() override;
+
+ virtual void setWindowState(Qt::WindowStates state) override;
+ bool checkNativeSurface(EGLDisplay display, EGLConfig config);
+
+ void applicationStateChanged(Qt::ApplicationState) override;
+ void propagateSizeHints() override;
+ bool setMouseGrabEnabled(bool grab) override;
+
+ void setVisible(bool visible) override;
+ QOhNativeWindow *nativeWindow() const;
+ QMargins frameMargins() const override;
+
+ void lower() override;
+ void raise() override;
+
+ int32_t nativeWindowId() const override;
+ virtual void setCursor(QCursorInfo *cursor) override;
+ virtual QCursorInfo *cursor() const override { return m_cursor; }
+ virtual bool startSystemMove() override;
+ void setWindowTitle(const QString &title) override;
+
+ bool isGeometryControlledBySystem() const;
+ void setNativeGeometry(const QRect &rect);
+ void applyCursor();
+ bool windowEvent(QEvent *event) override;
+protected:
+ bool setWindowState_sys(Qt::WindowStates state);
+ void createNativeWindow();
+ void showNativeWindow();
+ void hideNativeWindow();
+ void destroyNativeWindow();
+ void updateNodeRenderFit();
+ void createNativeWindowNode(WId nativeHandle = 0) override;
+ void updateXComponentBackground();
+ void updateUnderMouseWindow();
+ virtual void showNativeNode() override;
+ virtual void handleWindowStateChange(Qt::WindowStates states) override;
+ static bool canApplyNewCursor(const QWindow *w);
+ static QOhPlatformOpenGLWindow *platformWindowOfWindow(const QWindow *w);
+private:
+ EGLSurface m_eglSurface = EGL_NO_SURFACE;
+ QSurfaceFormat m_format;
+ QScopedPointer<QOhNativeWindow> m_nativeWindow;
+ static QOhNativeWindow *m_mouseGrab;
+ QRect m_normalRect;
+ QRect m_rect;
+ QRegion m_initMask;
+ QCursorInfo *m_cursor = nullptr;
+ std::optional<bool> m_focus;
+ std::optional<bool> m_requestActivate;
+ mutable Kind m_volatileWindowKind = Kind::InValid;
+ bool m_windowIsHidden = true;
+ bool m_minimized = false;
+};
+
+QT_END_NAMESPACE
+#endif // QOHPLATFORMOPENGLWINDOW_H
new file mode 100644
@@ -0,0 +1,25 @@
+#include <qpa/qplatformintegrationplugin.h>
+#include "qohplatformintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformIntegrationPlugin: public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "openharmony.json")
+public:
+ QPlatformIntegration *create(const QString &key, const QStringList ¶mList) override;
+};
+
+
+QPlatformIntegration *QOhPlatformIntegrationPlugin::create(const QString &key, const QStringList ¶mList)
+{
+ Q_UNUSED(paramList);
+ if (!key.compare(QLatin1String("openharmony"), Qt::CaseInsensitive))
+ return new QOhPlatformIntegration(paramList);
+ return 0;
+}
+
+QT_END_NAMESPACE
+#include "qohplatformplugin.moc"
+
new file mode 100644
@@ -0,0 +1,394 @@
+#include <QTime>
+#include <QStaticText>
+#include <qpa/qwindowsysteminterface.h>
+
+#include "qohplatformscreen.h"
+#include "qohplatformintegration.h"
+#include "qohplatformwindow.h"
+#include "qohplatformopenglwindow.h"
+#include "qohplatformcursor.h"
+#include "qohwindownode.h"
+#include "qohwindowcontext.h"
+#include "qoheventdispatcher.h"
+
+#include <qguiapplication.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QWindow>
+#include <private/qguiapplication_p.h>
+#include <private/qhighdpiscaling_p.h>
+#include <private/qjspromise_p.h>
+#include <QtGui/private/qwindow_p.h>
+#include <private/qt_egl_p.h> /* Qt6 only modify */
+#include <QImage>
+#include <QByteArray>
+#include <QtCore/qopenharmonydefines.h>
+#include "qohplatformwindow.h"
+#include "qohwindowcontext.h"
+#include <QTimer>
+#include <sys/mman.h>
+#if OHOS_SDK_VERSION > 13
+#include "qohdisplaymanager.h"
+#include "qohdisplay.h"
+#include <window_manager/oh_display_capture.h>
+#else
+#include "qjsscreen.h"
+#include <multimedia/image_framework/image/pixelmap_native.h>
+#endif
+#include <private/qopenharmony_p.h>
+#include "qohauxiliary.h"
+
+QT_BEGIN_NAMESPACE
+
+QDebug &operator<<(QDebug &outputStream, QOhPlatformScreen *platformScreen)
+{
+ outputStream << "QOhPlatformScreen("
+ << "id:" << platformScreen->displayId()
+ << "name:" << platformScreen->name()
+ << "geo:" << platformScreen->geometry()
+ << " " << platformScreen->refreshRate() << "hz"
+ << ")";
+ return outputStream;
+}
+
+#if OHOS_SDK_VERSION > 13
+QOhPlatformScreen::QOhPlatformScreen(QOhDisplay *display)
+ : QPlatformScreen(), m_cursor(new QOhPlatformCursor(this))
+ , m_display(display)
+{
+ m_display->setScreen(this);
+ m_availableGeometry = m_display->availableGeometry();
+ m_format = QImage::Format_RGBX8888;
+ m_depth = 32;
+ m_physicalSize = px2mm(m_display->physicalSize());
+ m_refreshRate = m_display->refreshRate();
+ QObject::connect(m_display, &QOhDisplay::orientationChanged, m_display, [this]{
+ handleScreenOrientationChange();
+ });
+
+ QObject::connect(m_display, &QOhDisplay::geometryChanged, m_display, [this]{
+ auto avaGeo = availableGeometry();
+ if (!avaGeo.isEmpty()) {
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), avaGeo);
+ }
+ handleScreenChanged();
+ });
+ QObject::connect(m_display, &QOhDisplay::availableGeometryChanged, m_display, [this]{
+ setAvailableGeometry(m_display->availableGeometry());
+ /* FIXME 屏幕排列布局改变后,通知Qt改变窗口的geometry数据 */
+ handleScreenChanged();
+ });
+ QObject::connect(m_display, &QOhDisplay::physicalSizeChanged, m_display, [this]{
+ setPhysicalSize(m_display->physicalSize());
+ });
+ QObject::connect(m_display, &QOhDisplay::refreshRateChanged, m_display, [this]{
+ handlerefreshRateChange(m_display->refreshRate());
+ });
+ QObject::connect(m_display, &QOhDisplay::scaledDensityChanged, m_display, [this]{
+ handleScreenLogicalDotsPerInchChange();
+ handleScreenChanged();
+ });
+}
+#else
+QOhPlatformScreen::QOhPlatformScreen(QJsScreen *jsScreen)
+ : QPlatformScreen(), m_cursor(new QOhPlatformCursor(this))
+ , m_jsScreen(jsScreen)
+{
+ m_jsScreen->attach(this);
+ m_availableGeometry = m_jsScreen->availableGeometry();
+
+ m_format = QImage::Format_RGBX8888;
+ m_depth = 32;
+ m_physicalSize = px2mm(m_jsScreen->physicalSize());
+ m_refreshRate = m_jsScreen->refreshRate();
+}
+#endif
+
+QOhPlatformScreen::~QOhPlatformScreen()
+{
+#if OHOS_SDK_VERSION > 13
+ m_display->setScreen(nullptr);
+ delete m_display;
+#else
+ m_jsScreen->attach(nullptr);
+ delete m_jsScreen;
+#endif
+}
+
+#if OHOS_SDK_VERSION > 13
+QOhDisplay *QOhPlatformScreen::display() const
+{
+ return m_display;
+}
+#endif
+
+QString QOhPlatformScreen::name() const
+{
+#if OHOS_SDK_VERSION > 13
+ return m_display->name();
+#else
+ return QPlatformScreen::name();
+#endif
+}
+
+QRect QOhPlatformScreen::geometry() const
+{
+#if OHOS_SDK_VERSION > 13
+ return m_display->geometry();
+#else
+ return QRect(QPoint(), m_jsScreen->size());
+#endif
+}
+
+QRect QOhPlatformScreen::availableGeometry() const
+{
+ return m_availableGeometry;
+}
+
+QWindow *QOhPlatformScreen::topLevelAt(const QPoint &p) const
+{
+ return QOhWindowContext::topLevelAt(const_cast<QOhPlatformScreen *>(this), p);
+}
+
+QPixmap QOhPlatformScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
+{
+ QPixmap pixmap;
+ QImage image;
+ int x = xIn;
+ int y = yIn;
+
+ QOhWindowNode *node = QOhWindowNode::fromId(window);
+ if (node) {
+ OH_PixelmapNative *pixelMap = node->grab();
+ if (pixelMap != nullptr) {
+ image = QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelMap);
+ OH_PixelmapNative_Release(pixelMap);
+ }
+ } else {
+#if OHOS_SDK_VERSION > 13
+ /* 截全屏 */
+ uint32_t id = m_display->id();
+ OH_PixelmapNative *pixelMap = nullptr;
+ NativeDisplayManager_ErrorCode code = OH_NativeDisplayManager_CaptureScreenPixelmap(id, &pixelMap);
+ if (code != DISPLAY_MANAGER_OK || pixelMap == nullptr) {
+ QString error;
+ switch (code) {
+ case DISPLAY_MANAGER_ERROR_NO_PERMISSION:
+ error = "no permission.";
+ break;
+ case DISPLAY_MANAGER_ERROR_INVALID_PARAM:
+ error = "parameter error.";
+ break;
+ case DISPLAY_MANAGER_ERROR_DEVICE_NOT_SUPPORTED:
+ error = "device not support.";
+ break;
+ case DISPLAY_MANAGER_ERROR_SYSTEM_ABNORMAL:
+ error = "display manager service works abnormally.";
+ break;
+ default:
+ break;
+ }
+ qWarning() << "capture screen failed:" << error;
+ return QPixmap();
+ }
+ image = QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelMap);
+ OH_PixelmapNative_Release(pixelMap);
+#else
+ //TODO:目前没有直接截全屏的接口(12 release beta1)
+ x = 0;
+ y = 0;
+ QJsModule screenshot("@ohos.screenshot");
+ image = QtOh::runOnJsUIThreadWithPromise<QImage>([&](auto p){
+ Napi::Value result = screenshot.call("pick");
+ if (result.IsPromise()) {
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([&, p](const Napi::CallbackInfo &info){
+ if (info.Length() < 1) {
+ p->set_value(QImage());
+ return ;
+ }
+ Napi::Object pickInfo = info[0].As<Napi::Object>();
+ Napi::Value pixelMap = pickInfo.Get("pixelMap");
+ OH_PixelmapNative *result = nullptr;
+ OH_PixelmapNative_ConvertPixelmapNativeFromNapi(env(), pixelMap, &result);
+ image = QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelMap);
+ p->set_value(image);
+ }.onCatch([p](const Napi::CallbackInfo &info){
+ Q_UNUSED(info);
+ p->set_value(QImage());
+ });
+ }
+ });
+#endif
+ }
+ if (image.isNull())
+ return QPixmap();
+
+ if (x <= 0 && y <= 0 && width <= 0 && height <= 0) {
+ QPixmap pix = QPixmap::fromImage(image);
+ return pix;
+ }
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ if (width < 0)
+ width = image.width() - x;
+ if (height < 0)
+ height = image.height() - y;
+
+ QImage croppedImage = image.copy(QRect(x, y, width, height));
+ pixmap = QPixmap::fromImage(croppedImage);
+ return pixmap;
+}
+
+QList<QPlatformScreen *> QOhPlatformScreen::virtualSiblings() const
+{
+ QList<QPlatformScreen *> result;
+#if OHOS_SDK_VERSION > 13
+ auto screens = QOhDisplayManager::instance()->allScreens();
+ for (int i = 0; i < screens.count(); i++) {
+ auto *screen = screens[i];
+ result << screen;
+ }
+#else
+ result << const_cast<QOhPlatformScreen *>(this);
+#endif
+ return result;
+}
+
+uint32_t QOhPlatformScreen::displayId()
+{
+#if OHOS_SDK_VERSION > 13
+ return m_display->id();
+#else
+ return m_jsScreen->id();
+#endif
+}
+
+void QOhPlatformScreen::setPhysicalSize(const QSize &size)
+{
+ m_physicalSize = px2mm(size);
+}
+
+void QOhPlatformScreen::setSize(const QSize &size)
+{
+ Q_UNUSED(size)
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
+}
+
+void QOhPlatformScreen::handleScreenLogicalDotsPerInchChange()
+{
+ QDpi dpi = logicalDpi();
+ QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(), dpi.first, dpi.second);
+ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void QOhPlatformScreen::handleScreenOrientationChange()
+{
+ QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation());
+ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void QOhPlatformScreen::handlerefreshRateChange(int refreshRate)
+{
+ m_refreshRate = refreshRate;
+ QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate);
+}
+
+void QOhPlatformScreen::handleScreenChanged()
+{
+ reportAllWindowGeometry();
+}
+
+void QOhPlatformScreen::reportAllWindowGeometry()
+{
+ auto allWindows = QOhWindowContext::allWindows();
+ for (auto *window : allWindows) {
+ if (QOhPlatformOpenGLWindow *w = dynamic_cast<QOhPlatformOpenGLWindow*>(window);
+ window->isTopLevelWindow() && w && w->nativeWindow()) {
+ /* 修改系统显示大小后最小化的窗口也需要处理geomtry变化,否则显示内容异常 */
+ if (w->screen() != this/* || Qt::WindowMinimized & w->windowStates()*/)
+ continue;
+ w->nativeWindow()->reportWindowGeometry();
+ }
+ }
+}
+
+QSizeF QOhPlatformScreen::px2mm(const QSize &size)
+{
+#if OHOS_SDK_VERSION > 13
+ qreal xdpi = m_display->xDPI();
+ qreal ydpi = m_display->yDPI();
+#else
+ qreal xdpi = m_jsScreen->xDPI();
+ qreal ydpi = m_jsScreen->yDPI();
+#endif
+ return QSizeF(size.width() * 25.4 / xdpi, size.height() * 25.4 / ydpi);
+}
+
+void QOhPlatformScreen::setAvailableGeometry(const QRect &rect)
+{
+ if (m_availableGeometry == rect)
+ return;
+
+ QRect oldGeometry = m_availableGeometry;
+ m_availableGeometry = rect;
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
+ // TODO 确定系统释放自动调整窗口大小
+ /* FIXME wanghao 折叠设备最大化到B、C面后,再次进入折叠态显示异常 */
+ //resizeMaximizedWindows();
+
+ if (oldGeometry.width() == 0 && oldGeometry.height() == 0 && rect.width() > 0 && rect.height() > 0) {
+ const auto allWindows = qGuiApp->allWindows();
+ for (auto *window : allWindows) {
+ if (window->handle()) {
+ QRect geometry = window->handle()->geometry();
+ if (geometry.width() > 0 && geometry.height() > 0)
+ QWindowSystemInterface::handleExposeEvent(window, QRect(QPoint(0, 0), geometry.size()));
+ }
+ }
+ }
+}
+
+static const int openharmonyLogicalDpi = 96;
+
+QDpi QOhPlatformScreen::logicalDpi() const
+{
+#if OHOS_SDK_VERSION > 13
+ qreal lDpi = m_display->scaledDensity() * openharmonyLogicalDpi;
+#else
+ qreal lDpi = m_jsScreen->scaledDensity() * openharmonyLogicalDpi;
+#endif
+ return QDpi(lDpi, lDpi);
+}
+
+QDpi QOhPlatformScreen::logicalBaseDpi() const
+{
+ return QDpi(openharmonyLogicalDpi, openharmonyLogicalDpi);
+}
+
+qreal QOhPlatformScreen::densityPixels() const
+{
+#if OHOS_SDK_VERSION > 13
+ return m_display->densityPixels();
+#else
+ return m_jsScreen->densityPixels();
+#endif
+}
+
+Qt::ScreenOrientation QOhPlatformScreen::orientation() const
+{
+#if OHOS_SDK_VERSION > 13
+ return m_display->orientation();
+#else
+ return m_jsScreen->orientation();
+#endif
+}
+
+Qt::ScreenOrientation QOhPlatformScreen::nativeOrientation() const
+{
+ return Qt::LandscapeOrientation;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,80 @@
+#ifndef QOHPLATFORMSCREEN_H
+#define QOHPLATFORMSCREEN_H
+
+#include <qpa/qplatformscreen.h>
+#include <native_window/external_window.h>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformWindow;
+#if OHOS_SDK_VERSION > 13
+class QOhDisplay;
+#else
+class QJsScreen;
+#endif
+
+class QOhPlatformScreen: public QPlatformScreen
+{
+public:
+#if OHOS_SDK_VERSION > 13
+ QOhPlatformScreen(QOhDisplay *display);
+#else
+ QOhPlatformScreen(QJsScreen *jsScreen);
+#endif
+ ~QOhPlatformScreen();
+
+#if OHOS_SDK_VERSION > 13
+ QOhDisplay *display() const;
+#endif
+
+ QString name() const override;
+ QRect geometry() const override;
+ QRect availableGeometry() const override;
+ int depth() const override { return m_depth; }
+ QImage::Format format() const override { return m_format; }
+ QSizeF physicalSize() const override { return m_physicalSize; }
+ qreal refreshRate() const override { return m_refreshRate; }
+ QDpi logicalDpi() const override;
+ QDpi logicalBaseDpi() const override;
+ qreal densityPixels() const;
+ Qt::ScreenOrientation orientation() const override;
+ Qt::ScreenOrientation nativeOrientation() const override;
+
+ QWindow *topLevelAt(const QPoint & p) const override;
+ QPixmap grabWindow(WId window, int xIn, int yIn, int width, int height) const override;
+
+ QList<QPlatformScreen *> virtualSiblings() const override;
+
+ using CursorPtr = QScopedPointer<QPlatformCursor>;
+ QPlatformCursor *cursor() const override { return m_cursor.data(); }
+
+ uint32_t displayId();
+ void setPhysicalSize(const QSize &size);
+ void setAvailableGeometry(const QRect &rect);
+ void setSize(const QSize &size);
+ void handleScreenLogicalDotsPerInchChange();
+ void handleScreenOrientationChange();
+ void handlerefreshRateChange(int refreshRate);
+ void handleScreenChanged();
+ void reportAllWindowGeometry();
+ friend QDebug &operator<<(QDebug &outputStream, QOhPlatformScreen *platformScreen);
+
+private:
+ QSizeF px2mm(const QSize &size);
+ QRect m_availableGeometry;
+ int m_depth;
+ QImage::Format m_format;
+ QSizeF m_physicalSize;
+ int m_refreshRate;
+
+ const CursorPtr m_cursor;
+#if OHOS_SDK_VERSION > 13
+ QOhDisplay *m_display;
+#else
+ QJsScreen *m_jsScreen;
+#endif
+};
+
+QT_END_NAMESPACE
+#endif
new file mode 100644
@@ -0,0 +1,121 @@
+#include "qohplatformservices.h"
+#include "qohnativewindowmanager.h"
+#include "qjscontext.h"
+#include "qopenharmony.h"
+#include <private/qopenharmony_p.h>
+
+#include <QUrl>
+#include <QThread>
+#include <QFileInfo>
+#include <QDebug>
+#include <QtCore/qopenharmonydefines.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QOhPlatformServices::openUrl(const QUrl &url)
+{
+ QString urlOrPath = url.toString(QUrl::FullyEncoded);
+ if (urlOrPath.startsWith("/")) {
+ // 以"/"开头的判断为本地文件
+ return openDocument(QUrl::fromLocalFile(urlOrPath));
+ }
+ QLatin1String scheme("https");
+ QUrl theUrl;
+ if (url.scheme().isEmpty()) {
+ theUrl.setScheme(scheme);
+ theUrl.setHost(urlOrPath);
+ } else {
+ theUrl = url;
+ }
+
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return false;
+ QtOh::runOnJsUIThreadAndWait([=]{
+ Napi::Object jsWant = Napi::Object::New(QtOh::uiEnv());
+
+ jsWant.Set("uri", theUrl.toString(QUrl::FullyEncoded).toStdString());
+ jsWant.Set("action", QString("ohos.want.action.viewData").toStdString());
+
+ Napi::Function callback = Napi::Function::New(jsWant.Env(), [](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ LOGW("startAbility failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = info[0].As<Napi::Object>();
+ int code = (int)error.Get("code").ToNumber();
+ if (code != 0)
+ LOGW("startAbility failed, code is %{public}d, message is %{public}s", code, error.Get("message").ToString().Utf8Value().c_str());
+ });
+ context->startAbility(jsWant, callback);
+ });
+ return true;
+}
+
+#define FLAG_AUTH_READ_URI_PERMISSION 0x00000001
+#define FLAG_AUTH_WRITE_URI_PERMISSION 0x00000002
+bool QOhPlatformServices::openDocument(const QUrl &url)
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return false;
+
+ QtOh::runOnJsUIThreadAndWait([=]{
+ QFileInfo info(url.toLocalFile());
+ bool isDir = info.isDir();
+ QString path = info.absoluteFilePath();
+ QString data = QtOh::uriFromPath(path);
+ if (data.isEmpty())
+ return;
+ Napi::Object jsWant = Napi::Object::New(QtOh::uiEnv());
+ if (isDir) {
+ jsWant.Set("abilityName", QString("MainAbility").toStdString());
+ jsWant.Set("bundleName", QString("com.huawei.hmos.filemanager").toStdString());
+ Napi::Object parameters = Napi::Object::New(jsWant.Env());
+ parameters.Set("fileUri", data.toStdString());
+ jsWant.Set("parameters", parameters);
+ } else {
+ jsWant.Set("action", QString("ohos.want.action.viewData").toStdString());
+ jsWant.Set("uri", data.toStdString());
+ // 给打开的应用对uri授权
+ jsWant.Set("flags", FLAG_AUTH_READ_URI_PERMISSION);
+ }
+ context->startAbility(jsWant);
+ });
+ return true;
+}
+
+bool QOhPlatformServices::openUri(const QUrl &url, bool isDir)
+{
+ QJsContext *context = qNativeWindowManager->context();
+ if (context == nullptr)
+ return false;
+
+ QtOh::runOnJsUIThreadAndWait([=]{
+ QString path = url.toString();
+ const char *cpath = path.toLocal8Bit().constData();
+ Napi::Object jsWant = Napi::Object::New(QtOh::uiEnv());
+ if (isDir) {
+ jsWant.Set("abilityName", QString("MainAbility").toStdString());
+ jsWant.Set("bundleName", QString("com.huawei.hmos.filemanager").toStdString());
+ Napi::Object parameters = Napi::Object::New(jsWant.Env());
+ parameters.Set("fileUri", cpath);
+ jsWant.Set("parameters", parameters);
+ } else {
+ jsWant.Set("action", QString("ohos.want.action.viewData").toStdString());
+ jsWant.Set("uri", cpath);
+ // 给打开的应用对uri授权
+ jsWant.Set("flags", FLAG_AUTH_READ_URI_PERMISSION);
+ }
+ context->startAbility(jsWant);
+ });
+ return true;
+}
+
+//TODO
+QByteArray QOhPlatformServices::desktopEnvironment() const
+{
+ return QByteArray();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,21 @@
+#ifndef QOHPLATFORMSERVICES_H
+#define QOHPLATFORMSERVICES_H
+
+#include <qpa/qplatformservices.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QOhPlatformServices : public QPlatformServices
+{
+public:
+ bool openUrl(const QUrl &url) override;
+ bool openDocument(const QUrl &url) override;
+ bool openUri(const QUrl &url, bool isDir) override;
+ QByteArray desktopEnvironment() const override;
+};
+
+
+QT_END_NAMESPACE
+
+#endif
new file mode 100644
@@ -0,0 +1,1484 @@
+#include <qcolor.h>
+#include <qpalette.h>
+#include <QCoreApplication>
+#include <QLoggingCategory>
+#include <QtCore/qjsmodule.h>
+#include <private/qopenharmony_p.h>
+#include <QtGui/private/qpainter_p.h>
+#include <qpa/qplatformintegration.h>
+#include <private/qguiapplication_p.h>
+#include <private/qohfontdatabase_p.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtGui/private/qabstractfileiconengine_p.h>
+
+#include <multimedia/image_framework/image/image_source_native.h>
+#include <multimedia/image_framework/image/pixelmap_native.h>
+#include <arkui/drawable_descriptor.h>
+#include <resourcemanager/ohresmgr.h>
+#include <resourcemanager/resmgr_common.h>
+#include "syscap_ndk.h"
+
+#include "qohauxiliary.h"
+#include "qohwindownode.h"
+#include "qohobjectholder.h"
+#include "qohplatformmenu.h"
+#include "qohplatformtheme.h"
+#include "qohsystemtrayicon.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatformopenglwindow.h"
+#include "qohplatformdialoghelpers.h"
+
+Q_LOGGING_CATEGORY(ohosthem, "openharmony.theme");
+
+namespace {
+
+QFont makeMenuFont()
+{
+ auto font = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont();
+ font.setPointSize(16);
+
+ return font;
+}
+
+QFont makeTitleFont()
+{
+ auto font = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont();
+ font.setPointSize(20);
+ font.setBold(true);
+
+ return font;
+}
+
+QFont makePushButtonFont()
+{
+ auto font = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont();
+ font.setPointSize(16);
+
+ return font;
+}
+
+QFont makeListViewFont()
+{
+ auto font = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont();
+ font.setPointSize(16);
+
+ return font;
+}
+
+QFont makeTipLabelFont()
+{
+ auto font = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont();
+ font.setPointSize(14);
+
+ return font;
+}
+
+QColor makeInactiveOrDisabledFromColor(const QColor &color)
+{
+ auto disabledColor = color;
+ disabledColor.setAlphaF(qBound(0.0, color.alphaF() * 0.4, 1.0));
+
+ return disabledColor;
+}
+
+class QAbstractTheme
+{
+public:
+ struct PalettesColors
+ {
+ QColor activeWindow;
+ QColor inactiveWindow;
+ QColor activeButtonText;
+ QColor inactiveButtonText;
+ QColor activeHighlight;
+ QColor inactiveHighlight;
+ QColor clickEffect;
+ QColor hover;
+ QColor foregroundContrary;
+ QColor highlighted;
+ QColor inactiveHighlighted;
+ QColor textPrimary;
+ QColor inactiveTextPrimary;
+ QColor inactiveText;
+ QColor componentNormal;
+ QColor componentActivated;
+ QColor indicatorArrow;
+ QColor scrollBarSlider;
+ QColor switchBgOff;
+ QColor switchOutlineOff;
+ QColor window;
+ };
+
+ struct SystemPaletteColors
+ {
+ QColor activeWindowFrame;
+ QColor inactiveWindowFrame;
+ QColor textHint;
+ QColor textTertiary;
+ };
+
+ struct MenuPaletteColors
+ {
+ QColor listSeparator;
+ QColor inactiveText;
+ };
+
+ struct HeaderPaletteColors
+ {
+ QColor activeButton;
+ };
+
+ struct TabBarPaletteColors
+ {
+ QColor activeWindowText;
+ QColor inactiveWindowText;
+ QColor disabledWindowText;
+ };
+
+ struct ButtonPaletteColors
+ {
+ QColor activeButton;
+ QColor inactiveButton;
+ QColor focusedOutline;
+ QColor enabledDefaultButtonText;
+ };
+
+ struct ToolButtonPaletteColors
+ {
+ QColor windowButtonBackground;
+ };
+
+ struct TextLineEditPaletteColors
+ {
+ QColor textEditBackground;
+ QColor inactiveTextEditBackground;
+ };
+
+ struct GroupboxPaletteColors
+ {
+ QColor background;
+ QColor groupBoxFrame;
+ QColor shadowXS;
+ };
+
+ struct TooltipPaletteColors
+ {
+ QColor toolTipBorder;
+ QColor toolTipBase;
+ };
+
+
+ struct AllPalletesColors
+ {
+ PalettesColors palettes;
+ ButtonPaletteColors button;
+ ToolButtonPaletteColors toolButton;
+ SystemPaletteColors system;
+ MenuPaletteColors menu;
+ TabBarPaletteColors tabBar;
+ TextLineEditPaletteColors textLineEdit;
+ GroupboxPaletteColors groupBox;
+ HeaderPaletteColors header;
+ TooltipPaletteColors toolTip;
+ };
+
+ using ThemeFonts = QHash<QPlatformTheme::Font, QFont>;
+ using ThemePalettes = QHash<QPlatformTheme::Palette, QPalette>;
+
+ explicit QAbstractTheme(ThemeFonts f, ThemePalettes p);
+ virtual ~QAbstractTheme();
+
+ virtual const QFont *font(QPlatformTheme::Font type) const;
+ virtual const QPalette *palette(QPlatformTheme::Palette type) const;
+
+protected:
+ static QPalette makeMenuPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeLabelPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeTabBarPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeButtonPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeSystemPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeHeaderPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeToolTipPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeTextEditPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeItemViewPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeComboBoxPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeGroupBoxPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeToolButtonPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeTextLineEditPalette(const AllPalletesColors &palettesColors);
+ static QPalette makeCheckBoxOrRadioButtonPalette(const AllPalletesColors &palettesColors);
+
+ static QPalette makePalette(
+ const QPalette &basePalette, std::initializer_list<std::tuple<QPalette::ColorGroup, QPalette::ColorRole, const QColor &>> brushEntries);
+
+ static QPalette makePalette(std::initializer_list<std::tuple<QPalette::ColorGroup, QPalette::ColorRole, const QColor &>> brushEntries);
+
+
+private:
+ ThemeFonts m_fonts;
+ ThemePalettes m_themesPalettes;
+};
+
+QAbstractTheme::QAbstractTheme(ThemeFonts f, ThemePalettes p)
+ : m_fonts(f)
+ , m_themesPalettes(p)
+{
+
+}
+
+QAbstractTheme::~QAbstractTheme()
+{
+ m_fonts.clear();
+ m_themesPalettes.clear();
+}
+
+const QFont *QAbstractTheme::font(QPlatformTheme::Font type) const
+{
+ const auto fontIter = m_fonts.find(type);
+ return fontIter != m_fonts.end()
+ ? &fontIter.value()
+ : nullptr;
+}
+
+const QPalette *QAbstractTheme::palette(QPlatformTheme::Palette type) const
+{
+ const auto it = m_themesPalettes.find(type);
+ if (it != m_themesPalettes.end()) {
+ return &it.value();
+ }
+ return nullptr;
+}
+
+QPalette QAbstractTheme::makeMenuPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::Window, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::Base, palettesColors.palettes.switchBgOff},
+ {QPalette::Inactive, QPalette::Button, palettesColors.palettes.switchOutlineOff},
+ {QPalette::Active, QPalette::Button, palettesColors.palettes.highlighted},
+
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.textPrimary},
+ {QPalette::Disabled, QPalette::Text, palettesColors.menu.inactiveText},
+ {QPalette::Inactive, QPalette::Text, palettesColors.menu.inactiveText},
+
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.clickEffect},
+
+ {QPalette::Active, QPalette::Mid, palettesColors.menu.listSeparator}
+ });
+}
+
+QPalette QAbstractTheme::makeLabelPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::WindowText, palettesColors.palettes.highlighted},
+ {QPalette::Inactive, QPalette::WindowText, palettesColors.palettes.inactiveText},
+ {QPalette::Disabled, QPalette::WindowText, palettesColors.palettes.inactiveHighlighted},
+
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveText},
+ {QPalette::Inactive, QPalette::Text, palettesColors.palettes.inactiveHighlighted},
+ });
+}
+
+QPalette QAbstractTheme::makeTabBarPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::WindowText, palettesColors.tabBar.activeWindowText},
+ {QPalette::Inactive, QPalette::WindowText, palettesColors.tabBar.inactiveWindowText},
+ {QPalette::Disabled, QPalette::WindowText, palettesColors.tabBar.disabledWindowText},
+
+ {QPalette::Active, QPalette::Window, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::Window, palettesColors.palettes.inactiveWindow},
+ {QPalette::Disabled, QPalette::Window, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Active, QPalette::Highlight, palettesColors.palettes.activeHighlight},
+ {QPalette::Inactive, QPalette::Highlight, palettesColors.palettes.inactiveHighlight},
+ {QPalette::Disabled, QPalette::Highlight, palettesColors.palettes.inactiveHighlight},
+ });
+}
+
+QPalette QAbstractTheme::makeButtonPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.componentActivated},
+
+ {QPalette::Active, QPalette::Button, palettesColors.button.activeButton},
+ {QPalette::Disabled, QPalette::Button, palettesColors.button.inactiveButton},
+ {QPalette::Inactive, QPalette::Button, palettesColors.button.inactiveButton},
+
+ {QPalette::Active, QPalette::ButtonText, palettesColors.palettes.activeButtonText},
+ {QPalette::Disabled, QPalette::ButtonText, palettesColors.palettes.inactiveButtonText},
+ {QPalette::Inactive, QPalette::ButtonText, palettesColors.palettes.inactiveButtonText},
+
+ {QPalette::All, QPalette::BrightText, palettesColors.button.enabledDefaultButtonText},
+
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.clickEffect},
+
+ {QPalette::Active, QPalette::Highlight, palettesColors.button.focusedOutline},
+ {QPalette::Active, QPalette::WindowText, palettesColors.palettes.indicatorArrow}
+ });
+}
+
+QPalette QAbstractTheme::makeSystemPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::Window, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::Window, palettesColors.palettes.inactiveWindow},
+ {QPalette::Inactive, QPalette::Window, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Active, QPalette::WindowText, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::WindowText, palettesColors.palettes.inactiveText},
+ {QPalette::Inactive, QPalette::WindowText, palettesColors.palettes.inactiveHighlighted},
+
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveText},
+ {QPalette::Inactive, QPalette::Text, palettesColors.palettes.inactiveHighlighted},
+
+ {QPalette::Active, QPalette::Base, palettesColors.system.activeWindowFrame},
+ {QPalette::Disabled, QPalette::Base, palettesColors.system.inactiveWindowFrame},
+ {QPalette::Inactive, QPalette::Base, palettesColors.system.inactiveWindowFrame},
+
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.textPrimary},
+
+ {QPalette::Active, QPalette::Highlight, palettesColors.palettes.highlighted},
+
+ {QPalette::All, QPalette::PlaceholderText, palettesColors.system.textHint},
+ {QPalette::Disabled, QPalette::PlaceholderText, palettesColors.system.textTertiary},
+
+ {QPalette::Active, QPalette::Midlight, palettesColors.palettes.scrollBarSlider},
+
+ {QPalette::Active, QPalette::Mid, palettesColors.palettes.componentNormal},
+ });
+}
+
+
+QPalette QAbstractTheme::makeHeaderPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::Button, palettesColors.header.activeButton},
+ {QPalette::Inactive, QPalette::Button, palettesColors.header.activeButton},
+ {QPalette::Disabled, QPalette::Button, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Current, QPalette::ButtonText, palettesColors.palettes.activeButtonText},
+ {QPalette::Active, QPalette::ButtonText, palettesColors.palettes.activeButtonText},
+ {QPalette::Inactive, QPalette::ButtonText, palettesColors.palettes.activeButtonText},
+ {QPalette::Disabled, QPalette::ButtonText, palettesColors.palettes.inactiveButtonText},
+ });
+}
+
+QPalette QAbstractTheme::makeToolTipPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::All, QPalette::Base, palettesColors.toolTip.toolTipBorder},
+ {QPalette::All, QPalette::ToolTipBase, palettesColors.toolTip.toolTipBase},
+ {QPalette::All, QPalette::ToolTipText, palettesColors.palettes.highlighted},
+ });
+}
+
+QPalette QAbstractTheme::makeTextEditPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ makeTextLineEditPalette(palettesColors),
+ {
+ {QPalette::All, QPalette::Base, palettesColors.palettes.activeWindow},
+ });
+}
+
+QPalette QAbstractTheme::makeItemViewPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::Base, palettesColors.palettes.window},
+ {QPalette::Inactive, QPalette::Base, palettesColors.palettes.window},
+ {QPalette::Disabled, QPalette::Base, palettesColors.palettes.window},
+
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::AlternateBase, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::AlternateBase, palettesColors.palettes.inactiveWindow},
+ {QPalette::Current, QPalette::AlternateBase, palettesColors.palettes.activeWindow},
+
+ {QPalette::Active, QPalette::Highlight, palettesColors.palettes.activeHighlight},
+ {QPalette::Inactive, QPalette::Highlight, palettesColors.palettes.activeHighlight},
+ {QPalette::Disabled, QPalette::Highlight, palettesColors.palettes.inactiveHighlight},
+
+ {QPalette::Active, QPalette::Window, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::Window, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::Window, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.activeHighlight},
+ {QPalette::Inactive, QPalette::Text, palettesColors.palettes.activeHighlight},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveHighlight},
+
+ {QPalette::Active, QPalette::HighlightedText, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::HighlightedText, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::HighlightedText, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::Dark, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::Dark, palettesColors.palettes.inactiveWindow},
+ {QPalette::Current, QPalette::Dark, palettesColors.palettes.activeWindow},
+ {QPalette::All, QPalette::Dark, palettesColors.palettes.activeWindow},
+
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Inactive, QPalette::Light, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::Light, palettesColors.palettes.inactiveWindow},
+
+ {QPalette::Active, QPalette::Mid, palettesColors.palettes.activeWindow},
+ {QPalette::Inactive, QPalette::Mid, palettesColors.palettes.activeWindow},
+ {QPalette::Disabled, QPalette::Mid, palettesColors.palettes.inactiveWindow},
+ {QPalette::Current, QPalette::Mid, palettesColors.palettes.activeWindow},
+ {QPalette::All, QPalette::Mid, palettesColors.palettes.activeWindow},
+ });
+}
+
+QPalette QAbstractTheme::makeComboBoxPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.componentActivated},
+ {QPalette::All, QPalette::Base, palettesColors.palettes.window},
+ {QPalette::All, QPalette::Window, palettesColors.palettes.window},
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveText},
+ {QPalette::Inactive, QPalette::Text, palettesColors.palettes.inactiveHighlighted},
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.clickEffect},
+ {QPalette::Active, QPalette::WindowText, palettesColors.palettes.indicatorArrow}
+ });
+}
+
+QPalette QAbstractTheme::makeGroupBoxPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Active, QPalette::Button, palettesColors.palettes.highlighted},
+ {QPalette::Inactive, QPalette::Button, palettesColors.palettes.switchOutlineOff},
+ {QPalette::Active, QPalette::Base, palettesColors.groupBox.groupBoxFrame},
+ {QPalette::Inactive, QPalette::Base, palettesColors.palettes.switchBgOff},
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.foregroundContrary},
+// {QPalette::Active, QPalette::Background, palettesColors.groupBox.background},
+// {QPalette::Inactive, QPalette::Background, palettesColors.groupBox.background},
+ {QPalette::Active, QPalette::Window, palettesColors.palettes.window},
+ {QPalette::Disabled, QPalette::Window, palettesColors.palettes.window},
+ {QPalette::Active, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Inactive, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveHighlighted},
+ {QPalette::Current, QPalette::Text, palettesColors.palettes.highlighted},
+ {QPalette::Disabled, QPalette::WindowText, palettesColors.palettes.inactiveHighlighted},
+ {QPalette::Active, QPalette::ButtonText, palettesColors.palettes.textPrimary},
+ {QPalette::Disabled, QPalette::ButtonText, palettesColors.palettes.inactiveHighlighted},
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.clickEffect},
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Active, QPalette::Shadow, palettesColors.groupBox.shadowXS},
+ {QPalette::Active, QPalette::Midlight, palettesColors.palettes.scrollBarSlider},
+ {QPalette::Active, QPalette::Mid, palettesColors.palettes.componentNormal}
+ });
+}
+
+QPalette QAbstractTheme::makeToolButtonPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ makeButtonPalette(palettesColors),
+ {
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.toolButton.windowButtonBackground},
+ });
+}
+
+QPalette QAbstractTheme::makeTextLineEditPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ makeSystemPalette(palettesColors),
+ {
+ {QPalette::Active, QPalette::AlternateBase, palettesColors.palettes.componentActivated},
+ {QPalette::All, QPalette::Base, palettesColors.textLineEdit.textEditBackground},
+ {QPalette::Disabled, QPalette::Base, palettesColors.textLineEdit.inactiveTextEditBackground},
+ {QPalette::All, QPalette::Text, palettesColors.palettes.textPrimary},
+ {QPalette::Disabled, QPalette::Text, palettesColors.palettes.inactiveTextPrimary},
+ });
+}
+
+QPalette QAbstractTheme::makeCheckBoxOrRadioButtonPalette(const AllPalletesColors &palettesColors)
+{
+ return makePalette(
+ {
+ {QPalette::Inactive, QPalette::Base, palettesColors.palettes.switchBgOff},
+ {QPalette::Inactive, QPalette::Button, palettesColors.palettes.switchOutlineOff},
+ {QPalette::Active, QPalette::Button, palettesColors.palettes.highlighted},
+ {QPalette::Active, QPalette::WindowText, palettesColors.palettes.highlighted},
+ {QPalette::Inactive, QPalette::WindowText, palettesColors.palettes.inactiveText},
+ {QPalette::Disabled, QPalette::WindowText, palettesColors.palettes.inactiveHighlighted},
+ {QPalette::Active, QPalette::Light, palettesColors.palettes.hover},
+ {QPalette::Active, QPalette::Dark, palettesColors.palettes.clickEffect},
+ });
+}
+
+QPalette QAbstractTheme::makePalette(const QPalette &basePalette, std::initializer_list<std::tuple<QPalette::ColorGroup, QPalette::ColorRole, const QColor &> > brushEntries)
+{
+ auto palette = basePalette;
+
+ for (const auto &brushEntry : brushEntries) {
+ palette.setBrush(
+ std::get<QPalette::ColorGroup>(brushEntry),
+ std::get<QPalette::ColorRole>(brushEntry),
+ std::get<const QColor &>(brushEntry));
+ }
+ return palette;
+}
+
+QPalette QAbstractTheme::makePalette(std::initializer_list<std::tuple<QPalette::ColorGroup, QPalette::ColorRole, const QColor &> > brushEntries)
+{
+ return makePalette(QPalette(), brushEntries);
+}
+
+class QOhDarkTheme : public QAbstractTheme
+{
+public:
+ explicit QOhDarkTheme();
+ ~QOhDarkTheme();
+
+private:
+ static PalettesColors makePalettesColorsDark();
+ static AllPalletesColors makeAllPalettesColorsDark();
+ static ButtonPaletteColors makeButtonPaletteColorsDark();
+ static TabBarPaletteColors makeTabBarPaletteColorsDark();
+ static HeaderPaletteColors makeHeaderPaletteColorsDark();
+ static TooltipPaletteColors makeTooltipPaletteColorsDark();
+ static GroupboxPaletteColors makeGroupboxPaletteColorsDark();
+ static SystemPaletteColors makeSystemButtonPaletteColorsDark();
+ static ToolButtonPaletteColors makeToolButtonPaletteColorsDark();
+ static TextLineEditPaletteColors makeTextLineEditPaletteColorsDark();
+ static MenuPaletteColors makeMenuPaletteColorsDark(const PalettesColors &palettes);
+};
+
+QOhDarkTheme::QOhDarkTheme() : QAbstractTheme({
+ {QPlatformTheme::MenuFont, makeMenuFont()},
+ {QPlatformTheme::MenuBarFont, makeMenuFont()},
+ {QPlatformTheme::MenuItemFont, makeMenuFont()},
+ {QPlatformTheme::TipLabelFont, makeTipLabelFont()},
+ {QPlatformTheme::ListViewFont, makeListViewFont()},
+ {QPlatformTheme::GroupBoxTitleFont, makeTitleFont()},
+ {QPlatformTheme::DockWidgetTitleFont, makeTitleFont()},
+ {QPlatformTheme::PushButtonFont, makePushButtonFont()},
+ }, {
+ []()->ThemePalettes {
+ const AllPalletesColors palettesColors = makeAllPalettesColorsDark();
+ return {
+ {QPlatformTheme::ButtonPalette, makeButtonPalette(palettesColors)},
+ {QPlatformTheme::CheckBoxPalette, makeCheckBoxOrRadioButtonPalette(palettesColors)},
+ {QPlatformTheme::ComboBoxPalette, makeComboBoxPalette(palettesColors)},
+ {QPlatformTheme::GroupBoxPalette, makeGroupBoxPalette(palettesColors)},
+ {QPlatformTheme::HeaderPalette, makeHeaderPalette(palettesColors)},
+ {QPlatformTheme::ItemViewPalette, makeItemViewPalette(palettesColors)},
+ {QPlatformTheme::LabelPalette, makeLabelPalette(palettesColors)},
+ {QPlatformTheme::MenuPalette, makeMenuPalette(palettesColors)},
+ {QPlatformTheme::RadioButtonPalette, makeCheckBoxOrRadioButtonPalette(palettesColors)},
+ {QPlatformTheme::SystemPalette, makeSystemPalette(palettesColors)},
+ {QPlatformTheme::TabBarPalette, makeTabBarPalette(palettesColors)},
+ {QPlatformTheme::TextEditPalette, makeTextEditPalette(palettesColors)},
+ {QPlatformTheme::TextLineEditPalette, makeTextLineEditPalette(palettesColors)},
+ {QPlatformTheme::ToolButtonPalette, makeToolButtonPalette(palettesColors)},
+ {QPlatformTheme::ToolTipPalette, makeToolTipPalette(palettesColors)},
+ };
+ }()
+ })
+{
+
+}
+
+QOhDarkTheme::~QOhDarkTheme()
+{
+
+}
+
+QAbstractTheme::PalettesColors QOhDarkTheme::makePalettesColorsDark()
+{
+ const auto activeWindow = QColor("#18181A");
+ const auto activeButtonText = QColor("#3F97E9");
+ const auto activeHighlight = QColor("#006CDE");
+ const auto highlighted = QColor("#3F97E9");
+ const auto textPrimary = QColor("#DBFFFFFF");
+
+ return {
+ .activeWindow = activeWindow,
+ .inactiveWindow = makeInactiveOrDisabledFromColor(activeWindow),
+ .activeButtonText = activeButtonText,
+ .inactiveButtonText = makeInactiveOrDisabledFromColor(activeButtonText),
+ .activeHighlight = activeHighlight,
+ .inactiveHighlight = makeInactiveOrDisabledFromColor(activeHighlight),
+ .clickEffect = QColor("#26FFFFFF"),
+ .hover = QColor("#19FFFFFF"),
+ .foregroundContrary = QColor("#E5E5E5"),
+ .highlighted = highlighted,
+ .inactiveHighlighted = makeInactiveOrDisabledFromColor(highlighted),
+ .textPrimary = textPrimary,
+ .inactiveTextPrimary = makeInactiveOrDisabledFromColor(textPrimary),
+ .inactiveText = QColor("#E6FFFFFF"),
+ .componentNormal = QColor("#19FFFFFF"),
+ .componentActivated = QColor("#3F97E9"),
+ .indicatorArrow = QColor("#DBFFFFFF"),
+ .scrollBarSlider = QColor("#FFFFFF"),
+ .switchBgOff = QColor("#33000000"),
+ .switchOutlineOff = QColor("#66FFFFFF"),
+ .window = QColor("#FF000000"),
+ };
+}
+
+QAbstractTheme::AllPalletesColors QOhDarkTheme::makeAllPalettesColorsDark()
+{
+ const auto palettes = makePalettesColorsDark();
+
+ return {
+ .palettes = palettes,
+ .button = makeButtonPaletteColorsDark(),
+ .toolButton = makeToolButtonPaletteColorsDark(),
+ .system = makeSystemButtonPaletteColorsDark(),
+ .menu = makeMenuPaletteColorsDark(palettes),
+ .tabBar = makeTabBarPaletteColorsDark(),
+ .textLineEdit = makeTextLineEditPaletteColorsDark(),
+ .groupBox = makeGroupboxPaletteColorsDark(),
+ .header = makeHeaderPaletteColorsDark(),
+ .toolTip = makeTooltipPaletteColorsDark(),
+ };
+}
+
+QAbstractTheme::ButtonPaletteColors QOhDarkTheme::makeButtonPaletteColorsDark()
+{
+ const auto activeButton = QColor("#19FFFFFF");
+
+ return {
+ .activeButton = activeButton,
+ .inactiveButton = makeInactiveOrDisabledFromColor(activeButton),
+ .focusedOutline = QColor("#3F97E9"),
+ .enabledDefaultButtonText = QColor("#E5E5E5"),
+ };
+}
+
+QAbstractTheme::TabBarPaletteColors QOhDarkTheme::makeTabBarPaletteColorsDark()
+{
+ const auto inactiveWindowText = QColor("#99FFFFFF");
+
+ return {
+ .activeWindowText = QColor("#FF3F97E9"),
+ .inactiveWindowText = inactiveWindowText,
+ .disabledWindowText = makeInactiveOrDisabledFromColor(inactiveWindowText),
+ };
+}
+
+QAbstractTheme::HeaderPaletteColors QOhDarkTheme::makeHeaderPaletteColorsDark()
+{
+ return {
+ .activeButton = QColor("#FF18181A"),
+ };
+}
+
+QAbstractTheme::TooltipPaletteColors QOhDarkTheme::makeTooltipPaletteColorsDark()
+{
+ return {
+ .toolTipBorder = QColor("#1AFFFFFF"),
+ .toolTipBase = QColor("#404040"),
+ };
+}
+
+QAbstractTheme::GroupboxPaletteColors QOhDarkTheme::makeGroupboxPaletteColorsDark()
+{
+ return {
+ .background = QColor("#00000000"),
+ .groupBoxFrame = QColor("#66FFFFFF"),
+ .shadowXS = QColor("#19FFFFFF"),
+ };
+}
+
+QAbstractTheme::SystemPaletteColors QOhDarkTheme::makeSystemButtonPaletteColorsDark()
+{
+ return {
+ .activeWindowFrame = QColor("#FF000000"),
+ .inactiveWindowFrame = QColor("#FF18181A"),
+ .textHint = QColor("#99FFFFFF"),
+ .textTertiary = QColor("#66FFFFFF"),
+ };
+}
+
+QAbstractTheme::ToolButtonPaletteColors QOhDarkTheme::makeToolButtonPaletteColorsDark()
+{
+ return {
+ .windowButtonBackground = QColor("#FFD0CFD5"),
+ };
+}
+
+QAbstractTheme::TextLineEditPaletteColors QOhDarkTheme::makeTextLineEditPaletteColorsDark()
+{
+ const auto textEditBackground = QColor("#19FFFFFF");
+ return {
+ .textEditBackground = textEditBackground,
+ .inactiveTextEditBackground = makeInactiveOrDisabledFromColor(textEditBackground),
+ };
+}
+
+QAbstractTheme::MenuPaletteColors QOhDarkTheme::makeMenuPaletteColorsDark(const PalettesColors &palettes)
+{
+ return {
+ .listSeparator = QColor("#33FFFFFF"),
+ .inactiveText = makeInactiveOrDisabledFromColor(palettes.textPrimary),
+ };
+}
+
+class QOhLightTheme : public QAbstractTheme
+{
+public:
+ explicit QOhLightTheme();
+ ~QOhLightTheme();
+
+private:
+ static PalettesColors makePalettesColorsLight();
+ static AllPalletesColors makeAllPalettesColorsLight();
+ static ButtonPaletteColors makeButtonPaletteColorsLight();
+ static TabBarPaletteColors makeTabBarPaletteColorsLight();
+ static HeaderPaletteColors makeHeaderPaletteColorsLight();
+ static TooltipPaletteColors makeTooltipPaletteColorsLight();
+ static GroupboxPaletteColors makeGroupboxPaletteColorsLight();
+ static SystemPaletteColors makeSystemButtonPaletteColorsLight();
+ static ToolButtonPaletteColors makeToolButtonPaletteColorsLight();
+ static TextLineEditPaletteColors makeTextLineEditPaletteColorsLight();
+ static MenuPaletteColors makeMenuPaletteColorsLight(const PalettesColors &palettes);
+};
+
+QOhLightTheme::QOhLightTheme() : QAbstractTheme({
+ {QPlatformTheme::MenuFont, makeMenuFont()},
+ {QPlatformTheme::MenuBarFont, makeMenuFont()},
+ {QPlatformTheme::MenuItemFont, makeMenuFont()},
+ {QPlatformTheme::TipLabelFont, makeTipLabelFont()},
+ {QPlatformTheme::ListViewFont, makeListViewFont()},
+ {QPlatformTheme::GroupBoxTitleFont, makeTitleFont()},
+ {QPlatformTheme::DockWidgetTitleFont, makeTitleFont()},
+ {QPlatformTheme::PushButtonFont, makePushButtonFont()},
+ }, {
+ []()->ThemePalettes {
+ const AllPalletesColors palettesColors = makeAllPalettesColorsLight();
+ return {
+ {QPlatformTheme::ButtonPalette, makeButtonPalette(palettesColors)},
+ {QPlatformTheme::CheckBoxPalette, makeCheckBoxOrRadioButtonPalette(palettesColors)},
+ {QPlatformTheme::ComboBoxPalette, makeComboBoxPalette(palettesColors)},
+ {QPlatformTheme::GroupBoxPalette, makeGroupBoxPalette(palettesColors)},
+ {QPlatformTheme::HeaderPalette, makeHeaderPalette(palettesColors)},
+ {QPlatformTheme::ItemViewPalette, makeItemViewPalette(palettesColors)},
+ {QPlatformTheme::LabelPalette, makeLabelPalette(palettesColors)},
+ {QPlatformTheme::MenuPalette, makeMenuPalette(palettesColors)},
+ {QPlatformTheme::RadioButtonPalette, makeCheckBoxOrRadioButtonPalette(palettesColors)},
+ {QPlatformTheme::SystemPalette, makeSystemPalette(palettesColors)},
+ {QPlatformTheme::TabBarPalette, makeTabBarPalette(palettesColors)},
+ {QPlatformTheme::TextEditPalette, makeTextEditPalette(palettesColors)},
+ {QPlatformTheme::TextLineEditPalette, makeTextLineEditPalette(palettesColors)},
+ {QPlatformTheme::ToolButtonPalette, makeToolButtonPalette(palettesColors)},
+ {QPlatformTheme::ToolTipPalette, makeToolTipPalette(palettesColors)},
+ };
+ }()
+ })
+{
+
+}
+
+QOhLightTheme::~QOhLightTheme()
+{
+
+}
+
+QAbstractTheme::PalettesColors QOhLightTheme::makePalettesColorsLight()
+{
+ const auto activeWindow = QColor("#FFFFFFFF");
+ const auto activeButtonText = QColor("#0A59F7");
+ const auto activeHighlight = QColor("#FF007DFF");
+ const auto highlighted = QColor("#FF0A59F7");
+ const auto textPrimary = QColor("#FF182431");
+
+ return {
+ .activeWindow = activeWindow,
+ .inactiveWindow = makeInactiveOrDisabledFromColor(activeWindow),
+ .activeButtonText = activeButtonText,
+ .inactiveButtonText = makeInactiveOrDisabledFromColor(activeButtonText),
+ .activeHighlight = activeHighlight,
+ .inactiveHighlight = makeInactiveOrDisabledFromColor(activeHighlight),
+ .clickEffect = QColor("#1A000000"),
+ .hover = QColor("#0D000000"),
+ .foregroundContrary = QColor("#FFFFFF"),
+ .highlighted = highlighted,
+ .inactiveHighlighted = makeInactiveOrDisabledFromColor(highlighted),
+ .textPrimary = textPrimary,
+ .inactiveTextPrimary = makeInactiveOrDisabledFromColor(textPrimary),
+ .inactiveText = QColor("#E6000000"),
+ .componentNormal = QColor("#19182431"),
+ .componentActivated = QColor("#007DFF"),
+ .indicatorArrow = QColor("#E5182431"),
+ .scrollBarSlider = QColor("#182431"),
+ .switchBgOff = QColor("#33FFFFFF"),
+ .switchOutlineOff = QColor("#66182431"),
+ .window = QColor("#FFF1F3F5"),
+ };
+};
+
+QAbstractTheme::AllPalletesColors QOhLightTheme::makeAllPalettesColorsLight()
+{
+ const auto palettes = makePalettesColorsLight();
+
+ return {
+ .palettes = palettes,
+ .button = makeButtonPaletteColorsLight(),
+ .toolButton = makeToolButtonPaletteColorsLight(),
+ .system = makeSystemButtonPaletteColorsLight(),
+ .menu = makeMenuPaletteColorsLight(palettes),
+ .tabBar = makeTabBarPaletteColorsLight(),
+ .textLineEdit = makeTextLineEditPaletteColorsLight(),
+ .groupBox = makeGroupboxPaletteColorsLight(),
+ .header = makeHeaderPaletteColorsLight(),
+ .toolTip = makeTooltipPaletteColorsLight(),
+ };
+}
+
+QAbstractTheme::ButtonPaletteColors QOhLightTheme::makeButtonPaletteColorsLight()
+{
+ const auto activeButton = QColor("#0C182431");
+
+ return {
+ .activeButton = activeButton,
+ .inactiveButton = makeInactiveOrDisabledFromColor(activeButton),
+ .focusedOutline = QColor("#007DFF"),
+ .enabledDefaultButtonText = QColor("#FFFFFFFF"),
+ };
+};
+
+QAbstractTheme::TabBarPaletteColors QOhLightTheme::makeTabBarPaletteColorsLight()
+{
+ const auto inactiveWindowText = QColor("#99182431");
+
+ return {
+ .activeWindowText = QColor("#FF007DFF"),
+ .inactiveWindowText = inactiveWindowText,
+ .disabledWindowText = makeInactiveOrDisabledFromColor(inactiveWindowText),
+ };
+};
+
+QAbstractTheme::HeaderPaletteColors QOhLightTheme::makeHeaderPaletteColorsLight()
+{
+ return {
+ .activeButton = QColor("#FFFFFFFF"),
+ };
+};
+
+QAbstractTheme::TooltipPaletteColors QOhLightTheme::makeTooltipPaletteColorsLight()
+{
+ return {
+ .toolTipBorder = QColor("#1A000000"),
+ .toolTipBase = QColor("#FFFFFFFF"),
+ };
+};
+
+QAbstractTheme::GroupboxPaletteColors QOhLightTheme::makeGroupboxPaletteColorsLight()
+{
+ return {
+ .background = QColor("#00000000"),
+ .groupBoxFrame = QColor("#66000000"),
+ .shadowXS = QColor("#19000000"),
+ };
+};
+
+QAbstractTheme::SystemPaletteColors QOhLightTheme::makeSystemButtonPaletteColorsLight()
+{
+ return {
+ .activeWindowFrame = QColor("#FFE4E4E4"),
+ .inactiveWindowFrame = QColor("#FFF2F3F5"),
+ .textHint = QColor("#99182431"),
+ .textTertiary = QColor("#66182431"),
+ };
+};
+
+QAbstractTheme::ToolButtonPaletteColors QOhLightTheme::makeToolButtonPaletteColorsLight()
+{
+ return {
+ .windowButtonBackground = QColor("#FFD0CFD5"),
+ };
+};
+
+QAbstractTheme::TextLineEditPaletteColors QOhLightTheme::makeTextLineEditPaletteColorsLight()
+{
+ const auto textEditBackground = QColor("#0C182431");
+ return {
+ .textEditBackground = textEditBackground,
+ .inactiveTextEditBackground = makeInactiveOrDisabledFromColor(textEditBackground),
+ };
+};
+
+QAbstractTheme::MenuPaletteColors QOhLightTheme::makeMenuPaletteColorsLight(const PalettesColors &palettes)
+{
+ return {
+ .listSeparator = QColor("#1A000000"),
+ .inactiveText = makeInactiveOrDisabledFromColor(palettes.textPrimary),
+ };
+};
+
+static QPalette qt_harmonyPalette()
+{
+ QColor base(250, 250, 250);
+ QColor text(24, 36, 49);
+ QColor background(250, 250, 250);
+ QColor light = background.lighter(150);
+ QColor mid(background.darker(130));
+ QColor midLight = mid.lighter(110);
+ QColor disabledBase(background);
+ QColor dark = background.darker(150);
+ QColor darkDisabled = dark.darker(110);
+ QColor highlightedText = Qt::black;
+ QColor disabledText = QColor(190, 190, 190);
+ QColor button(241, 241, 241);
+ QColor shadow(201, 201, 201);
+ QColor highlight(0, 120, 215);
+ QColor disabledShadow = shadow.lighter(150);
+
+ QPalette harmonyPalette = QPalette(Qt::black, background, light, dark, mid, text, base);
+ harmonyPalette.setBrush(QPalette::Button, button);
+ harmonyPalette.setBrush(QPalette::Shadow, shadow);
+ harmonyPalette.setBrush(QPalette::Midlight, midLight);
+ harmonyPalette.setBrush(QPalette::HighlightedText, highlightedText);
+ harmonyPalette.setColor(QPalette::Disabled, QPalette::Text, Qt::gray);
+ harmonyPalette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray);
+
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText);
+
+ harmonyPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight);
+ harmonyPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight);
+ harmonyPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlight.lighter(150));
+ return harmonyPalette;
+}
+
+class QOhDefaultTheme : public QAbstractTheme
+{
+public:
+ explicit QOhDefaultTheme();
+ ~QOhDefaultTheme();
+
+private:
+ static PalettesColors makePalettesColorsDefault();
+ static AllPalletesColors makeAllPalettesColorsDefault();
+ static ButtonPaletteColors makeButtonPaletteColorsDefault();
+ static TabBarPaletteColors makeTabBarPaletteColorsDefault();
+ static HeaderPaletteColors makeHeaderPaletteColorsDefault();
+ static TooltipPaletteColors makeTooltipPaletteColorsDefault();
+ static GroupboxPaletteColors makeGroupboxPaletteColorsDefault();
+ static SystemPaletteColors makeSystemButtonPaletteColorsDefault();
+ static ToolButtonPaletteColors makeToolButtonPaletteColorsDefault();
+ static TextLineEditPaletteColors makeTextLineEditPaletteColorsDefault();
+ static MenuPaletteColors makeMenuPaletteColorsDefault(const PalettesColors &palettes);
+};
+/* TODO 暂时用同一种配色? */
+QOhDefaultTheme::QOhDefaultTheme() : QAbstractTheme({
+ []()->ThemeFonts {
+ const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ const auto *fontDatabase = static_cast<QOhFontDatabase *>(platformIntegration->fontDatabase());
+ return fontDatabase->themeFonts();
+ }()
+ }, {
+ []()->ThemePalettes {
+ const AllPalletesColors palettesColors = makeAllPalettesColorsDefault();
+ Q_UNUSED(palettesColors)
+ return {
+ {QPlatformTheme::ButtonPalette, qt_harmonyPalette()},
+ {QPlatformTheme::CheckBoxPalette, qt_harmonyPalette()},
+ {QPlatformTheme::ComboBoxPalette, qt_harmonyPalette()},
+ {QPlatformTheme::GroupBoxPalette, qt_harmonyPalette()},
+ {QPlatformTheme::HeaderPalette, qt_harmonyPalette()},
+ {QPlatformTheme::ItemViewPalette, qt_harmonyPalette()},
+ {QPlatformTheme::LabelPalette, qt_harmonyPalette()},
+ {QPlatformTheme::MenuPalette, qt_harmonyPalette()},
+ {QPlatformTheme::RadioButtonPalette, qt_harmonyPalette()},
+ {QPlatformTheme::SystemPalette, qt_harmonyPalette()},
+ {QPlatformTheme::TabBarPalette, qt_harmonyPalette()},
+ {QPlatformTheme::TextEditPalette, qt_harmonyPalette()},
+ {QPlatformTheme::TextLineEditPalette, qt_harmonyPalette()},
+ {QPlatformTheme::ToolButtonPalette, qt_harmonyPalette()},
+ {QPlatformTheme::ToolTipPalette, qt_harmonyPalette()},
+ };
+ }()
+ })
+{
+
+}
+
+QOhDefaultTheme::~QOhDefaultTheme()
+{
+
+}
+
+QAbstractTheme::PalettesColors QOhDefaultTheme::makePalettesColorsDefault()
+{
+ const auto activeWindow = QColor("#f0f0f0");
+ const auto activeButtonText = QColor("#353535");
+ const auto activeHighlight = QColor("#3daee9");
+ const auto highlighted = QColor("#3daee9");
+ const auto textPrimary = QColor("#353535");
+
+ return {
+ .activeWindow = activeWindow,
+ .inactiveWindow = QColor("#e0e0e0"),
+ .activeButtonText = activeButtonText,
+ .inactiveButtonText = QColor("#7f8c8d"),
+ .activeHighlight = activeHighlight,
+ .inactiveHighlight = QColor("#93cde9"),
+ .clickEffect = QColor("#1a000000"),
+ .hover = QColor("#0d3daee9"),
+ .foregroundContrary = QColor("#ffffff"),
+ .highlighted = highlighted,
+ .inactiveHighlighted = QColor("#93cde9"),
+ .textPrimary = textPrimary,
+ .inactiveTextPrimary = QColor("#7f8c8d"),
+ .inactiveText = QColor("#7f8c8d"),
+ .componentNormal = QColor("#e0e0e0"),
+ .componentActivated = QColor("#3daee9"),
+ .indicatorArrow = QColor("#353535"),
+ .scrollBarSlider = QColor("#c0c0c0"),
+ .switchBgOff = QColor("#e6e6e6"),
+ .switchOutlineOff = QColor("#b1b1b1"),
+ .window = QColor("#eff0f1"),
+ };
+}
+
+QAbstractTheme::AllPalletesColors QOhDefaultTheme::makeAllPalettesColorsDefault()
+{
+ const auto palettes = makePalettesColorsDefault();
+
+ return {
+ .palettes = palettes,
+ .button = makeButtonPaletteColorsDefault(),
+ .toolButton = makeToolButtonPaletteColorsDefault(),
+ .system = makeSystemButtonPaletteColorsDefault(),
+ .menu = makeMenuPaletteColorsDefault(palettes),
+ .tabBar = makeTabBarPaletteColorsDefault(),
+ .textLineEdit = makeTextLineEditPaletteColorsDefault(),
+ .groupBox = makeGroupboxPaletteColorsDefault(),
+ .header = makeHeaderPaletteColorsDefault(),
+ .toolTip = makeTooltipPaletteColorsDefault(),
+ };
+}
+
+QAbstractTheme::ButtonPaletteColors QOhDefaultTheme::makeButtonPaletteColorsDefault()
+{
+ const auto activeButton = QColor("#fcfcfc");
+
+ return {
+ .activeButton = activeButton,
+ .inactiveButton = QColor("#e0e0e0"),
+ .focusedOutline = QColor("#3daee9"),
+ .enabledDefaultButtonText = QColor("#353535"),
+ };
+};
+
+QAbstractTheme::TabBarPaletteColors QOhDefaultTheme::makeTabBarPaletteColorsDefault()
+{
+ return {
+ .activeWindowText = QColor("#3daee9"),
+ .inactiveWindowText = QColor("#7f8c8d"),
+ .disabledWindowText = QColor("#bdc3c7"),
+ };
+};
+
+QAbstractTheme::HeaderPaletteColors QOhDefaultTheme::makeHeaderPaletteColorsDefault()
+{
+ return {
+ .activeButton = QColor("#eff0f1"),
+ };
+};
+
+QAbstractTheme::TooltipPaletteColors QOhDefaultTheme::makeTooltipPaletteColorsDefault()
+{
+ return {
+ .toolTipBorder = QColor("#999999"),
+ .toolTipBase = QColor("#FFFFFF"),
+ };
+};
+
+QAbstractTheme::GroupboxPaletteColors QOhDefaultTheme::makeGroupboxPaletteColorsDefault()
+{
+ return {
+ .background = QColor("#00000000"),
+ .groupBoxFrame = QColor("#999999"),
+ .shadowXS = QColor("#19000000"),
+ };
+};
+
+QAbstractTheme::SystemPaletteColors QOhDefaultTheme::makeSystemButtonPaletteColorsDefault()
+{
+ return {
+ .activeWindowFrame = QColor("#e6e6e6"),
+ .inactiveWindowFrame = QColor("#eff0f1"),
+ .textHint = QColor("#999999"),
+ .textTertiary = QColor("#808080"),
+ };
+};
+
+QAbstractTheme::ToolButtonPaletteColors QOhDefaultTheme::makeToolButtonPaletteColorsDefault()
+{
+ return {
+ .windowButtonBackground = QColor("#E6E6E6"),
+ };
+};
+
+QAbstractTheme::TextLineEditPaletteColors QOhDefaultTheme::makeTextLineEditPaletteColorsDefault()
+{
+ const auto textEditBackground = QColor("#ffffff");
+ return {
+ .textEditBackground = textEditBackground,
+ .inactiveTextEditBackground = QColor("#eff0f1"),
+ };
+};
+
+QAbstractTheme::MenuPaletteColors QOhDefaultTheme::makeMenuPaletteColorsDefault(const PalettesColors &palettes)
+{
+ Q_UNUSED(palettes)
+ return {
+ .listSeparator = QColor("#e6e6e6"),
+ .inactiveText = QColor("#7f8c8d"),
+ };
+};
+constexpr int mouseDoubleClickDistance = 60;
+constexpr int mouseDoubleClickInterval = 300;
+
+}
+
+class QOhFileIconEngine : public QAbstractFileIconEngine
+{
+public:
+ explicit QOhFileIconEngine(const QFileInfo &info, QPlatformTheme::IconOptions opts) :
+ QAbstractFileIconEngine(info, opts) {
+
+ }
+
+ static QList<QSize> availableIconSizes()
+ {
+ // Todo get from system
+ const qreal devicePixelRatio = qGuiApp->devicePixelRatio();
+ const int sizes[] = {
+ qRound(16 * devicePixelRatio), qRound(32 * devicePixelRatio),
+ qRound(64 * devicePixelRatio), qRound(128 * devicePixelRatio),
+ qRound(256 * devicePixelRatio)
+ };
+ return QAbstractFileIconEngine::toSizeList(sizes, sizes + sizeof(sizes) / sizeof(sizes[0]));
+ }
+
+ QList<QSize> availableSizes(QIcon::Mode = QIcon::Normal, QIcon::State = QIcon::Off) override
+ { return QOhFileIconEngine::availableIconSizes(); }
+
+ static QJsModule *m_fileManagerService;
+protected:
+ QString cacheKey() const override;
+ QPixmap filePixmap(const QSize &size, QIcon::Mode mode, QIcon::State) override;
+};
+
+QJsModule *QOhFileIconEngine::m_fileManagerService = nullptr;
+
+QOhPlatformTheme::QOhPlatformTheme()
+ : m_colorMode(ColorMode::Default)
+ , m_themes{
+ {ColorMode::Dark, new QOhDarkTheme()},
+ {ColorMode::Light, new QOhLightTheme()},
+ {ColorMode::Default, new QOhDefaultTheme()}
+ }
+{
+ /*openharmony 无该能力*/
+ if(canIUse("SystemCapability.FileManagement.FileManagerService.Core")){
+ QOhFileIconEngine::m_fileManagerService = new QJsModule("@hms.filemanagement.fileManagerService");
+ }
+}
+
+QOhPlatformTheme::~QOhPlatformTheme()
+{
+ qDeleteAll(m_themes);
+ m_themes.clear();
+ if (QOhFileIconEngine::m_fileManagerService != nullptr)
+ delete QOhFileIconEngine::m_fileManagerService;
+}
+
+void QOhPlatformTheme::setColorMode(ColorMode mode)
+{
+ if (!m_themes.contains(mode)) {
+ qCWarning(ohosthem, "%s: unsupported ColorsTheme (%d), ignoring the call",
+ Q_FUNC_INFO, static_cast<int>(mode));
+ return;
+ }
+ m_colorMode = mode;
+}
+
+QOhPlatformTheme::ColorMode QOhPlatformTheme::colorMode() const
+{
+ return m_colorMode;
+}
+
+QString QOhPlatformTheme::standardButtonText(int button) const
+{
+ return QPlatformTheme::standardButtonText(button);
+}
+
+bool QOhPlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const
+{
+ return type == QPlatformTheme::FileDialog;
+}
+
+const QPalette *QOhPlatformTheme::palette(Palette type) const
+{
+ QAbstractTheme *theme = m_themes.value(m_colorMode, nullptr);
+ if (nullptr != theme) {
+ return theme->palette(type);
+ }
+ return QPlatformTheme::palette(type);
+}
+
+QPlatformDialogHelper *QOhPlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const
+{
+ return QOhPlatformDialogHelpers::createHelper(type);
+}
+
+const QFont *QOhPlatformTheme::font(Font type) const
+{
+ QAbstractTheme *theme = m_themes.value(m_colorMode, nullptr);
+ if (nullptr != theme) {
+ return theme->font(type);
+ }
+
+ return QPlatformTheme::font(type);
+}
+
+#ifndef QT_NO_SYSTEMTRAYICON
+QPlatformSystemTrayIcon *QOhPlatformTheme::createPlatformSystemTrayIcon() const
+{
+ return new QOhSystemTrayIcon();
+}
+#endif
+
+QPixmap QOhPlatformTheme::standardPixmap(QPlatformTheme::StandardPixmap sp, const QSizeF &size) const
+{
+ return QPlatformTheme::standardPixmap(sp, size);
+}
+
+QString QOhFileIconEngine::cacheKey() const
+{
+ // Cache directories unless custom or drives, which have custom icons depending on type
+ if ((options() & QPlatformTheme::DontUseCustomDirectoryIcons) && fileInfo().isDir() && !fileInfo().isRoot())
+ return QStringLiteral("qt_/directory/");
+ if (!fileInfo().isFile())
+ return QString();
+ // Return "" for .exe, .lnk and .ico extensions.
+ // It is faster to just look at the file extensions;
+ // avoiding slow QFileInfo::isExecutable() (QTBUG-13182)
+ QString suffix = fileInfo().suffix();
+ if (!suffix.compare(u"exe", Qt::CaseInsensitive)
+ || !suffix.compare(u"lnk", Qt::CaseInsensitive)
+ || !suffix.compare(u"ico", Qt::CaseInsensitive)) {
+ return QString();
+ }
+ return QLatin1String("qt_.")
+ + (suffix.isEmpty() ? fileInfo().fileName() : std::move(suffix).toUpper()); // handle "Makefile";)
+}
+
+class QOhDrawableDescriptor
+{
+public:
+ QOhDrawableDescriptor(ArkUI_DrawableDescriptor *descriptor)
+ : m_descriptor(new QOhObjectHolder<ArkUI_DrawableDescriptor>(descriptor, OH_ArkUI_DrawableDescriptor_Dispose))
+ {}
+
+ QPixmap toQPixmap(const QSize &size) const
+ {
+ auto pixelMap = OH_ArkUI_DrawableDescriptor_GetStaticPixelMap(m_descriptor->object());
+ if (pixelMap == nullptr)
+ return QPixmap();
+ QOhObjectHolder<OH_PixelmapNative> pixelMapHolder(pixelMap, OH_PixelmapNative_Release);
+ auto image = QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelMap);
+ return QPixmap::fromImage(image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ }
+
+private:
+ QScopedPointer<QOhObjectHolder<ArkUI_DrawableDescriptor>> m_descriptor;
+};
+
+std::vector<std::string> split(const std::string& s, char delimiter) {
+ std::vector<std::string> tokens;
+ size_t start = 0;
+ size_t end = s.find(delimiter);
+
+ while (end != std::string::npos) {
+ tokens.push_back(s.substr(start, end - start));
+ start = end + 1;
+ end = s.find(delimiter, start);
+ }
+
+ tokens.push_back(s.substr(start));
+ return tokens;
+}
+
+QPixmap QOhFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State)
+{
+ const QString filePath = QDir::toNativeSeparators(fileInfo().filePath());
+ QPixmap pixmap = QtOh::runOnJsUIThreadWithResult([](const QString &suffix, const QSize &size){
+ static QJsModule uniformTypeDescriptor("@ohos.data.uniformTypeDescriptor");
+ QString ohosSuffix = "." + suffix;
+ Napi::Value id = uniformTypeDescriptor.call("getUniformDataTypeByFilenameExtension", {Napi::String::New(uniformTypeDescriptor.env(), ohosSuffix.toStdString())});
+ if (id.IsEmpty() && !m_fileManagerService)
+ return QPixmap();
+ Napi::Value result = m_fileManagerService->call(QString::fromUtf8("getFileIconSync"), {id});
+ std::string uri;
+ ResourceManager_ErrorCode code = SUCCESS;
+ auto mgr = qNativeWindowManager->resourceManager();
+ QJsObject jsMgr(qNativeWindowManager->jsResourceManager().ToObject());
+ ArkUI_DrawableDescriptor *descriptor = nullptr;
+ Napi::Value jsDescriptor = Napi::Value();
+ if (result.IsString()) {
+ uri = result.ToString().Utf8Value();
+
+ /*note:c接口最后的OH_ArkUI_DrawableDescriptor_GetStaticPixelMap获取失败,使用ts接口*/
+ jsDescriptor = jsMgr.call("getDrawableDescriptorByName",
+ { Napi::String::New(jsMgr.env(), uri.c_str()),
+ Napi::Number::New(jsMgr.env(), uint8_t(0)),
+ Napi::Number::New(jsMgr.env(), uint8_t(1))});
+
+ // code = OH_ResourceManager_GetDrawableDescriptorDataByName(mgr, uri.c_str(),
+ // &descriptor, 0, 0);
+ } else if (result.IsObject()) {
+ Napi::Object obj = result.As<Napi::Object>();
+ int id = (int32_t)obj.Get("id").ToNumber();
+ if (id != -1 ) {
+
+ jsDescriptor = jsMgr.call("getDrawableDescriptor",
+ { Napi::Number::New(jsMgr.env(), uint8_t(id)),
+ Napi::Number::New(jsMgr.env(), uint8_t(0)),
+ Napi::Number::New(jsMgr.env(), uint8_t(1))});
+
+ // code = OH_ResourceManager_GetDrawableDescriptor(mgr, id, &descriptor);
+ } else {
+ Napi::Array params = obj.Get("params").As<Napi::Array>();
+ Napi::Value name = params[uint32_t(0)];
+ Napi::String nameString = name.As<Napi::String>();
+ auto names = split(nameString.Utf8Value(), '.');
+ if (names.size() != 3) {
+ qWarning() << "GetDrawableDescriptor failed, resource name is invalid";
+ return QPixmap();
+ }
+
+ qWarning() << "filePixmap resource udmf type:" << names.back().c_str();
+ jsDescriptor = jsMgr.call("getDrawableDescriptorByName",
+ { Napi::String::New(jsMgr.env(), names.back().c_str()),
+ Napi::Number::New(jsMgr.env(), uint8_t(0)),
+ Napi::Number::New(jsMgr.env(), uint8_t(1)) });
+
+ // code = OH_ResourceManager_GetDrawableDescriptorDataByName(mgr,names.back().c_str(),
+ // &descriptor, 0, 1);
+ }
+ }
+ // if (code != SUCCESS) {
+ // qWarning() << "GetDrawableDescriptor failed: error code" << code;
+ // return QPixmap();
+ // }
+ // QOhDrawableDescriptor drawableDescriptor(descriptor);
+ // return drawableDescriptor.toQPixmap(size);
+
+ if(jsDescriptor.IsObject()){
+ Napi::Value pixelMap = QJsObject(jsDescriptor.ToObject()).call("getPixelMap");
+ OH_PixelmapNative *pixelmapNative = nullptr;
+ OH_PixelmapNative_ConvertPixelmapNativeFromNapi(jsMgr.env(), pixelMap, &pixelmapNative);
+ if (pixelmapNative != nullptr) {
+ QOhObjectHolder<OH_PixelmapNative> pixelMapHolder(pixelmapNative, OH_PixelmapNative_Release);
+ auto image = QtOh::UdmfHelper::createQImageFromPixelmapNative(pixelmapNative);
+ return QPixmap::fromImage(image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ }
+ }
+ return QPixmap();
+ }, fileInfo().suffix().toLower(), size);
+ if (pixmap.isNull()) {
+ qWarning("QOhFileIconEngine::filePixmap() no icon found");
+ }
+ return pixmap;
+}
+
+QIcon QOhPlatformTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const
+{
+ if (QtOh::apiVersion() >= 17) {
+ return QIcon(new QOhFileIconEngine(fileInfo, iconOptions));
+ }
+
+ return QPlatformTheme::fileIcon(fileInfo, iconOptions);
+}
+
+bool QOhPlatformTheme::darkThemeEnabled() const
+{
+ return this->colorMode() == QOhPlatformTheme::ColorMode::Dark;
+}
+
+void QOhPlatformTheme::setThemeColorMode(int colorMode)
+{
+ setColorMode(ColorMode(colorMode));
+
+ const auto allWindows = qGuiApp->allWindows();
+ if (allWindows.empty()) {
+ QWindowSystemInterface::handleThemeChange(nullptr);
+ } else {
+ for (auto *window : allWindows) {
+
+ /* NOTE quickwindow窗口不能设置XComponent背景色
+ * 设置后可能导致渲染异常
+ */
+ if (QSurface::OpenGLSurface != window->surfaceType()) {
+ if (auto handle = dynamic_cast<QOhPlatformOpenGLWindow*>(window->handle())) {
+ QVariant var = window->property("_q_background");
+ QBrush brush = var.value<QBrush>();
+
+ /* NOTE 窗口没有设置过XComponent背景色才应用主题色 */
+ if (Qt::NoBrush == brush.style()) {
+ const QColor &cr = m_themes.value(ColorMode(colorMode))->palette(Palette::SystemPalette)->color(QPalette::Window);
+ handle->nativeWindowNode()->setBackgroundColor(cr);
+ } else {
+ handle->nativeWindowNode()->setBackgroundColor(qbrush_color(brush));
+ }
+ }
+ }
+ QWindowSystemInterface::handleThemeChange(window);
+ }
+ }
+}
+
+QVariant QOhPlatformTheme::themeHint(ThemeHint hint) const
+{
+ switch (hint) {
+ case WheelScrollLines:
+ return QVariant(wheelScrollLines);
+ case StyleNames:
+ return QVariant(QStringList{ "harmonyos", "openharmony" }); /* FIXME 暂时认为OpenHarmony HarmonyOS是两种不同的主题样式 */
+ case MouseDoubleClickDistance:
+ case TouchDoubleTapDistance:
+ {
+ auto *primaryScreen = qGuiApp->primaryScreen();
+ auto *platformScreen = primaryScreen != nullptr
+ ? primaryScreen->handle()
+ : nullptr;
+ return platformScreen != nullptr
+ ? QHighDpi::fromNativePixels(
+ QHighDpi::toNative(
+ mouseDoubleClickDistance, QtOh::densityPixels(platformScreen)),
+ platformScreen)
+ : mouseDoubleClickDistance;
+ }
+ case MouseDoubleClickInterval:
+ return mouseDoubleClickInterval;
+ case UseFullScreenForPopupMenu:
+ return QVariant(QtOh::isTabletDevice());
+ default:
+ return QPlatformTheme::themeHint(hint);
+ }
+}
new file mode 100644
@@ -0,0 +1,53 @@
+#ifndef QOHPLATFORMTHEME_H
+#define QOHPLATFORMTHEME_H
+
+#include <QtCore/qvariant.h>
+#include <QtCore/QMap>
+#include <QtCore/QHash>
+#include <qpa/qplatformtheme.h>
+
+namespace {
+class QAbstractTheme;
+}
+
+class QOhPlatformTheme: public QPlatformTheme
+{
+ friend class QOhEventDispatcherPrivate;
+public:
+ enum class ColorMode {
+ Dark,
+ Light,
+ Default
+ };
+
+ QOhPlatformTheme();
+ ~QOhPlatformTheme();
+
+ ColorMode colorMode() const;
+ void setColorMode(ColorMode mode);
+
+ QVariant themeHint(ThemeHint hint) const override;
+ virtual QString standardButtonText(int button) const override;
+ bool usePlatformNativeDialog(DialogType type) const override;
+ const QPalette *palette(Palette type = SystemPalette) const override;
+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
+ virtual const QFont *font(Font type = SystemFont) const override;
+#ifndef QT_NO_SYSTEMTRAYICON
+ virtual QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
+#endif
+ virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
+ virtual QIcon fileIcon(const QFileInfo &fileInfo,
+ QPlatformTheme::IconOptions iconOptions = { }) const override;
+
+ bool darkThemeEnabled() const override;
+ void setThemeColorMode(int colorMode) override;
+
+private:
+ ColorMode m_colorMode;
+ inline static int wheelScrollLines = 3;
+ QMap<ColorMode, QAbstractTheme*> m_themes;
+ mutable QHash<QPlatformTheme::Font, QFont> m_fonts;
+ QMap<ColorMode, QHash<Palette, QPalette>> m_themesPalettes;
+};
+
+#endif // QOHPLATFORMTHEME_H
new file mode 100644
@@ -0,0 +1,52 @@
+#include <qopenharmonydefines.h>
+#include "qohplatformvulkaninstance.h"
+#include <vulkan/vulkan_ohos.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QOhPlatformVulkanInstance::QOhPlatformVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance)
+ , m_createSurface(nullptr)
+{
+ m_lib.setFileName(QStringLiteral("vulkan"));
+
+ if (!m_lib.load()) {
+ LOGW("Failed to load %{public}s", qPrintable(m_lib.fileName()));
+ return;
+ }
+
+ init(&m_lib);
+}
+
+void QOhPlatformVulkanInstance::createOrAdoptInstance()
+{
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_OHOS_surface") << QByteArrayLiteral("VK_KHR_surface"));
+}
+
+QOhPlatformVulkanInstance::~QOhPlatformVulkanInstance()
+{
+}
+
+VkSurfaceKHR QOhPlatformVulkanInstance::createSurface(void *window)
+{
+ VkSurfaceKHR surface = VK_NULL_HANDLE;
+ if (!m_createSurface) {
+ m_createSurface = reinterpret_cast<PFN_vkCreateSurfaceOHOS>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateSurfaceOHOS"));
+ }
+ if (!m_createSurface) {
+ qWarning("Failed to find vkCreateSurfaceOHOS");
+ return surface;
+ }
+ VkSurfaceCreateInfoOHOS surfaceCreateInfo = {};
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS;
+ surfaceCreateInfo.window = reinterpret_cast<OHNativeWindow *>(window);
+ int err = m_createSurface(m_vkInst, &surfaceCreateInfo, NULL, &surface);
+ if (err != VK_SUCCESS) {
+ qWarning() << "create vulkan surface failed";
+ }
+ return surface;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,27 @@
+#ifndef QOHPLATFORMVULKANINSTANCE_H
+#define QOHPLATFORMVULKANINSTANCE_H
+
+#include <QtGui/private/qbasicvulkanplatforminstance_p.h>
+#include <QLibrary>
+#include <vulkan/vulkan_ohos.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPlatformVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QOhPlatformVulkanInstance(QVulkanInstance *instance);
+ ~QOhPlatformVulkanInstance();
+
+ void createOrAdoptInstance() override;
+ VkSurfaceKHR createSurface(void *window);
+
+private:
+ QVulkanInstance *m_instance;
+ QLibrary m_lib;
+ PFN_vkCreateSurfaceOHOS m_createSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHPLATFORMVULKANINSTANCE_H
new file mode 100644
@@ -0,0 +1,416 @@
+#include <QDebug>
+#include <QMetaObject>
+#include <qguiapplication.h>
+#include <qpa/qplatformwindow_p.h>
+#include <QtGui/qguiapplication.h>
+#include <private/qguiapplication_p.h>
+#include <QtCore/qopenharmonydefines.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
+
+#include "qohevent.h"
+#include "qohmain.h"
+#include "qohauxiliary.h"
+#include "qohwindownode.h"
+#include "qohplatformwindow.h"
+#include "qohplatformscreen.h"
+#include "qoheventdispatcher.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatforminputcontext.h"
+#include "qohnativewindow.h"
+#include "qohwindowcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+#define FullUpdateRequest QEvent::User + 1
+
+QOhPlatformWindow::QOhPlatformWindow(QWindow *window)
+ : QPlatformWindow(window)
+ , m_node(nullptr)
+{
+ m_windowState = window->windowState();
+}
+
+QOhPlatformWindow::~QOhPlatformWindow()
+{
+ deleteNativeWindowNode();
+ QOhWindowContext::removeWindow(this);
+}
+
+void QOhPlatformWindow::initialize()
+{
+ QOhWindowContext::appendWindow(this);
+}
+
+void QOhPlatformWindow::setOpacity(qreal level)
+{
+ /* TODO 需要控制整个窗体的透明度
+ * 鸿蒙因安全问题,不开放此权限
+ */
+ QPlatformWindow::setOpacity(level);
+}
+
+Qt::WindowModality QOhPlatformWindow::modality() const
+{
+ Qt::WindowModality m = window()->modality();
+ return m;
+}
+
+bool QOhPlatformWindow::isExposed() const
+{
+ return m_isExposed;
+}
+
+WId QOhPlatformWindow::winId() const
+{
+ if (!m_node) return (WId)nullptr;
+ return m_node->id();
+}
+
+QSize QOhPlatformWindow::xcomponentSize() const
+{
+ if (m_node) {
+ QSize size = QHighDpi::fromNativePixels(m_node->surfaceSize(), window());
+ return size;
+ }
+ return QPlatformWindow::xcomponentSize();
+}
+
+void QOhPlatformWindow::lower()
+{
+ if (m_node != nullptr) {
+ m_node->lower();
+ }
+ QOhWindowContext::lowerWindow(this);
+}
+
+void QOhPlatformWindow::raise()
+{
+ if (m_node != nullptr) {
+ m_node->raise();
+ }
+ QOhWindowContext::raiseWindow(this);
+}
+
+void QOhPlatformWindow::setGeometry(const QRect &rect)
+{
+ QPlatformWindow::setGeometry(rect);
+ if (m_node != nullptr) {
+ m_node->setGeometry(rect);
+ }
+}
+
+bool QOhPlatformWindow::isTopLevelWindow() const
+{
+ return window()->isTopLevel();
+}
+
+bool QOhPlatformWindow::hasFrame() const
+{
+ return detectionWindowHasFrame();
+}
+
+bool QOhPlatformWindow::isModal() const
+{
+ return window()->isModal();
+}
+
+bool QOhPlatformWindow::showWithoutActive() const
+{
+ const QVariant showWithoutActivating = window()->property("_q_showWithoutActivating");
+ bool without = showWithoutActivating.isValid() && showWithoutActivating.toBool();
+
+ Qt::WindowType type = window()->type();
+ // 添加QMenu显示不获取焦点 QMenu type为Qt::Popup
+ return type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || without;
+}
+
+bool QOhPlatformWindow::canSetFocus() const
+{
+ Qt::WindowType type = window()->type();
+
+ /* QDockWidget窗口类型为Qt::Tool,可以获取焦点 */
+ bool result = (type != Qt::Popup)
+ && (type != Qt::ToolTip);
+ return result;
+}
+
+QString QOhPlatformWindow::parentWindowName() const
+{
+ QOhPlatformWindow *p = dynamic_cast<QOhPlatformWindow *>(parent());
+ if (p == nullptr)
+ return QString();
+ return p->windowName();
+}
+
+QString QOhPlatformWindow::transientParentName() const
+{
+ QWindow *w = this->window();
+ if (Q_NULLPTR == w)
+ return QString();
+
+ QWindow * transient = w->transientParent();
+ if (transient) {
+ if (QOhPlatformWindow *p = dynamic_cast<QOhPlatformWindow*>(transient->handle())) {
+ return p->windowName();
+ }
+ }
+ return QString();
+}
+
+QOhPlatformScreen *QOhPlatformWindow::platformScreen() const
+{
+ return static_cast<QOhPlatformScreen *>(screen());
+}
+
+QString QOhPlatformWindow::windowTitle() const
+{
+ QString title = window()->title();
+ return formatWindowTitle(title, " - ");
+}
+
+void QOhPlatformWindow::setWindowName(const QString &name)
+{
+ m_name = name;
+}
+
+QString QOhPlatformWindow::windowName() const
+{
+ if (m_name.isEmpty()) {
+ m_name = QOhWindowContext::uniqueName(this);
+ }
+ return m_name;
+}
+
+bool QOhPlatformWindow::isRaster() const
+{
+ if (isForeignWindow())
+ return false;
+
+ return window()->surfaceType() == QSurface::RasterSurface
+ || window()->surfaceType() == QSurface::RasterGLSurface;
+}
+
+void QOhPlatformWindow::applicationStateChanged(Qt::ApplicationState)
+{
+ QRegion region;
+ if (isExposed())
+ region = QRect(QPoint(), geometry().size());
+
+ fireExpose(region);
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+void QOhPlatformWindow::attachRootComponent(void *component)
+{
+ if (m_node != nullptr)
+ m_node->attachRootComponent(component);
+}
+
+QString QOhPlatformWindow::componentName() const
+{
+ if (m_node != nullptr)
+ return m_node->componentName();
+ return QString();
+}
+
+void QOhPlatformWindow::setVisible(bool visible)
+{
+ if (visible) {
+ fireFullExpose(true);
+ showNativeNode();
+ } else {
+ hideNativeNode();
+ if (QOhWindowContext::mouseGrabberWindow() == window())
+ QOhWindowContext::setMouseGrabberWindow(nullptr);
+ fireExpose(QRegion());
+ }
+ // QPlatformWindow::setVisible(visible);中调用了QWindowSystemInterface::flushWindowSystemEvents();
+ // 会先处理系统消息,比如鼠标事件,这用会导致时序错误等问题,比如菜单栏上移动鼠标,当前活动菜单项隐藏之后会调用该接口
+ // 如果使用flushWindowSystemEvents会导致又去处理鼠标事件,导致菜单项隐藏后续操作在处理系统鼠标之后,发生逻辑混乱问题
+ //QPlatformWindow::setVisible(visible);
+}
+
+bool QOhPlatformWindow::setMouseGrabEnabled(bool grab)
+{
+ QOhWindowContext::setMouseGrabberWindow(grab ? window() : nullptr);
+ return true;
+}
+
+bool QOhPlatformWindow::setKeyboardGrabEnabled(bool grab)
+{
+ QOhWindowContext::setKeyGrabberWindow(grab ? window() : nullptr);
+ return true;
+}
+
+void QOhPlatformWindow::handleWindowStatusEvent(QtOh::WindowStatusType event)
+{
+ HiTracer tracer("QOhPlatformWindow::handleWindowStatusEvent");
+ switch (event) {
+ case QtOh::WindowStatusType::UNDEFINED:
+ return;
+ case QtOh::WindowStatusType::MINIMIZE:
+ handleWindowStateChange(m_windowState | Qt::WindowMinimized);
+ break;
+ case QtOh::WindowStatusType::MAXIMIZE:
+ handleWindowStateChange(Qt::WindowMaximized | (m_windowState.testFlag(Qt::WindowFullScreen) ? Qt::WindowFullScreen : Qt::WindowNoState));
+ break;
+ case QtOh::WindowStatusType::FLOATING:
+ handleWindowStateChange(Qt::WindowNoState);
+ break;
+ case QtOh::WindowStatusType::SPLIT_SCREEN:
+ case QtOh::WindowStatusType::FULL_SCREEN:
+ handleWindowStateChange(Qt::WindowFullScreen | (m_windowState.testFlag(Qt::WindowMaximized) ? Qt::WindowMaximized : Qt::WindowNoState));
+ break;
+ case QtOh::WindowStatusType::SURFACE_SHOW:
+ /* STATUS_SURFACE_SHOW暂不处理,最小化窗口或获取winId的窗口在显示时会额外触发多个窗口回调,
+ * 由windowStatusChange进行通知 */
+ return;
+ default:
+ return;
+ }
+}
+
+Qt::WindowStates QOhPlatformWindow::windowStates() const
+{
+ return m_windowState;
+}
+
+void QOhPlatformWindow::fireFullExpose(bool async)
+{
+ m_isExposed = true;
+ if (async) {
+ fireExpose(QRect(QRect(QPoint(), geometry().size())));
+ } else {
+ QWindow *w = window();
+ QEvent *e = new QEvent(w->inherits("QQuickWindow") ? QEvent::Type(FullUpdateRequest) : QEvent::UpdateRequest);
+ QCoreApplication::postEvent(w, e, Qt::HighEventPriority);
+ }
+}
+
+void QOhPlatformWindow::fireExpose(const QRegion ®ion)
+{
+ m_isExposed = !region.isEmpty();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
+}
+
+QOhWindowNode *QOhPlatformWindow::nativeWindowNode() const
+{
+ return m_node;
+}
+
+void QOhPlatformWindow::createNativeWindowNode(WId nativeHandle)
+{
+ if (nativeHandle) {
+ m_node = QOhWindowNode::fromId(nativeHandle);
+ } else {
+ QOhPlatformWindow *parentWindow = dynamic_cast<QOhPlatformWindow*>(parent());
+ m_node = new QOhWindowNode(parentWindow == nullptr ? nullptr : parentWindow->nativeWindowNode());
+ }
+ if (m_node != nullptr)
+ m_node->attachPlatformWindow(this);
+}
+
+void QOhPlatformWindow::deleteNativeWindowNode()
+{
+ if (m_node != nullptr) {
+ m_node->attachPlatformWindow(nullptr);
+ delete m_node;
+ m_node = nullptr;
+ }
+}
+
+void QOhPlatformWindow::showNativeNode()
+{
+ if (m_node != nullptr) {
+ m_node->setVisible(true);
+ }
+}
+
+void QOhPlatformWindow::hideNativeNode()
+{
+ if (m_node != nullptr) {
+ m_node->setVisible(false);
+ }
+}
+
+bool QOhPlatformWindow::detectionWindowHasFrame() const
+{
+ bool topLevel = isTopLevelWindow();
+ Qt::WindowFlags flags = window()->flags();
+ Qt::WindowType type = static_cast<Qt::WindowType>(int(flags) & Qt::WindowType_Mask);
+ bool dialog = false;
+ bool tool = false;
+ bool popup = false;
+ switch (type) {
+ case Qt::Dialog:
+ case Qt::Sheet:
+ dialog = true;
+ break;
+ case Qt::Drawer:
+ case Qt::Tool:
+ tool = true;
+ break;
+ case Qt::Popup:
+ popup = true;
+ break;
+ default:
+ break;
+ }
+
+ if ((flags & Qt::MSWindowsFixedSizeDialogHint))
+ dialog = true;
+
+ if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
+ return false;
+ } else if (topLevel) {
+ if (flags & Qt::FramelessWindowHint)
+ return false; // no border
+ else
+ return true;
+ } else {
+ return false;
+ }
+ return false;
+}
+
+void QOhPlatformWindow::setParent(const QPlatformWindow *window)
+{
+ if (m_node == nullptr)
+ return;
+
+ QOhWindowNode *parentNode = nullptr;
+ if (window != nullptr){
+ WId parentWid = window->winId();
+ parentNode = QOhWindowNode::fromId(parentWid);
+ }
+
+ m_node->setParent(parentNode);
+ m_node->raise();
+}
+
+void QOhPlatformWindow::handleWindowStateChange(Qt::WindowStates states)
+{
+ if (Qt::Popup == window()->type()) {
+ /* FIXME 鸿蒙最小化状态和Qt不一致
+ * 造成QMenu菜单显示异常
+ */
+ return;
+ }
+
+ QWindowSystemInterface::handleWindowStateChanged(window(), states, m_windowState);
+ m_windowState = states;
+ if (states.testFlag(Qt::WindowMinimized)) {
+ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
+ }
+}
+
+void QOhPlatformWindow::setNativeNodeRenderFit(QOpenHarmonyWindow::NativeNodeRenderFit fit)
+{
+ if (nullptr != m_node)
+ m_node->setRenderFit(static_cast<ArkUI_RenderFit>(fit));
+ else
+ window()->setProperty(QOpenHarmonyWindow::NativeNodeFitIdentifier, QVariant(static_cast<int>(fit)));
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,90 @@
+#ifndef QOHPLATFORMWINDOW_H
+#define QOHPLATFORMWINDOW_H
+#include <qrect.h>
+#include <qobject.h>
+#include <qscopedpointer.h>
+#include <qpa/qplatformwindow.h>
+#include <qpa/qplatformwindow_p.h> /* Qt6 only modify */
+
+#include "qohauxiliary.h"
+
+QT_BEGIN_NAMESPACE
+class QOhWindowNode;
+class QOhPlatformScreen;
+class QOhPlatformBackingStore;
+struct OH_NativeXComponent;
+struct QCursorInfo;
+
+class QOhPlatformWindow : public QPlatformWindow,
+ public QNativeInterface::Private::QOpenHarmonyWindow /* Qt6 only modify */
+{
+
+public:
+ explicit QOhPlatformWindow(QWindow *window);
+ ~QOhPlatformWindow();
+
+ void initialize() override;
+ void setOpacity(qreal level) override;
+ virtual Qt::WindowModality modality() const;
+ virtual bool isExposed() const override;
+ WId winId() const override;
+ QSize xcomponentSize() const override;
+ void lower() override;
+ void raise() override;
+ void setGeometry(const QRect &rect) override;
+ QOhPlatformScreen *platformScreen() const;
+
+ QString windowTitle() const;
+ void setWindowName(const QString &name);
+ QString windowName() const;
+
+ bool isRaster() const;
+
+ virtual void applicationStateChanged(Qt::ApplicationState);
+ virtual void setFrameMargins(const QMargins &margins){Q_UNUSED(margins);}
+ virtual void attachRootComponent(void *component);
+ virtual QString componentName() const;
+ virtual void setCursor(QCursorInfo *cursor) {Q_UNUSED(cursor);}
+ virtual QCursorInfo *cursor() const { return nullptr; }
+ void setVisible(bool visible) override;
+ bool setMouseGrabEnabled(bool grab) override;
+ bool setKeyboardGrabEnabled(bool grab) override;
+ void setNativeNodeRenderFit(QOpenHarmonyWindow::NativeNodeRenderFit fit) override; /* Qt6 only modify */
+
+ bool isTopLevelWindow() const;
+
+ bool hasFrame() const;
+
+ bool isModal() const;
+
+ bool showWithoutActive() const;
+
+ bool canSetFocus() const;
+
+ QString parentWindowName() const;
+ QString transientParentName() const;
+
+ void handleWindowStatusEvent(QtOh::WindowStatusType event);
+ Qt::WindowStates windowStates() const;
+ void setParent(const QPlatformWindow *window) override;
+ virtual int32_t nativeWindowId() const { return 0; }
+
+ void fireFullExpose(bool async = false);
+ void fireExpose(const QRegion ®ion = QRegion());
+ QOhWindowNode *nativeWindowNode() const;
+protected:
+ virtual void createNativeWindowNode(WId nativeHandle = 0);
+ void deleteNativeWindowNode();
+ virtual void showNativeNode();
+ virtual void hideNativeNode();
+ bool detectionWindowHasFrame() const;
+ virtual void handleWindowStateChange(Qt::WindowStates states);
+
+ mutable QString m_name;
+ Qt::WindowStates m_windowState = Qt::WindowNoState;
+ bool m_isExposed = false;
+ QOhWindowNode *m_node;
+};
+
+QT_END_NAMESPACE
+#endif // QOHPLATFORMWINDOW_H
new file mode 100644
@@ -0,0 +1,571 @@
+#include "qohsystemtrayicon.h"
+#include "qohnativewindowmanager.h"
+#include "qohplatformmenu.h"
+#include <QDataStream>
+#include <QDateTime>
+#include <QTimer>
+#include <QDebug>
+#include "qjscontext.h"
+#include "qohutility.h"
+#include "qohjsonlistener.h"
+#include <functional>
+#include <private/qguiapplication_p.h>
+#include "qohauxiliary.h"
+#include <qpa/qplatformtheme.h>
+#include <multimedia/image_framework/image/pixelmap_native.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOhSystemTrayIcon
+ \brief openHarmony native system tray icon
+
+ \internal
+*/
+
+QOhSystemTrayIcon::QOhSystemTrayIcon()
+ :m_isInit(false)
+{
+ qRegisterMetaType<QPlatformSystemTrayIcon::ActivationReason>("QPlatformSystemTrayIcon::ActivationReason");
+
+}
+
+QOhSystemTrayIcon::~QOhSystemTrayIcon()
+{
+ if (m_statusManage) {
+ delete m_statusManage;
+ m_statusManage = nullptr;
+ }
+
+ if (m_isInit)
+ g_hasTrayIcon = false;
+
+ m_isInit = false;
+}
+
+void QOhSystemTrayIcon::init()
+{
+ if (!isSystemTrayAvailable()) {
+ qWarning() << "Creating tray icon on a tablet or phone is not supported!";
+ return;
+ }
+
+ if (g_hasTrayIcon || m_isInit)
+ return;
+
+ if (m_iconLight.isNull() || m_iconDark.isNull())
+ return;
+
+ QtOh::runOnJsUIThreadAndWait([this]{
+ m_statusManage = new QJsModule("@hms.pcService.statusBarManager");
+ Napi::Object icon = toNativeTrayIcon();
+ if (icon.IsNull() || icon.IsEmpty())
+ return;
+
+ QString abilityName = QtOh::apiVersion() >= 14 ? "" : qNativeWindowManager->getAbilityName();
+ QString moduleName = qNativeWindowManager->getModuleName();
+ Napi::Object operation = Napi::Object::New(QtOh::uiEnv());
+ operation.Set("abilityName", abilityName.toStdString());
+ operation.Set("title", m_toolTip.toStdString());
+ operation.Set("height", 50);
+ operation.Set("moduleName", moduleName.toStdString());
+
+ /*添加菜单项*/
+ Napi::Array menus = m_menu ? createOhMenu(m_menu) : createDefaultMenu();
+
+ /* 添加托盘图标 */
+ Napi::Object item = Napi::Object::New(QtOh::uiEnv());
+ item.Set("icons", icon);
+ item.Set("quickOperation", operation);
+ item.Set("statusBarGroupMenu", menus);
+
+ QJsContext *context = qNativeWindowManager->context();
+ if (context) {
+ m_statusManage->call("addToStatusBar", {context->object(), item});
+
+ if (m_statusManage->hasError()) {
+ qWarning() << "addToStatusBar failed:" << m_statusManage->lastError();
+ return;
+ }
+ g_hasTrayIcon = true;
+ m_isInit = true;
+ }
+
+ /* 设置回调 */
+ if (QtOh::apiVersion() >= 14) {
+ QJsObject::QNapiCallBack func1, func2;
+ func1 = std::bind(&QOhSystemTrayIcon::onStatusBarIconClick, this, std::placeholders::_1);
+ func2 = std::bind(&QOhSystemTrayIcon::onRightMenuClick, this, std::placeholders::_1);
+
+ m_statusBarIconClickListener = new QOhJsOnListener(m_statusManage, "statusBarIconClick", func1);
+ m_statusBarIconClickListener->on();
+ m_rightMenuClickListener = new QOhJsOnListener(m_statusManage, "rightMenuClick", func2);
+ m_rightMenuClickListener->on();
+ }
+ });
+}
+
+void QOhSystemTrayIcon::cleanup()
+{
+ if (m_statusManage) {
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if (m_statusBarIconClickListener != nullptr) {
+ m_statusBarIconClickListener->off();
+ delete m_statusBarIconClickListener;
+ m_statusBarIconClickListener = nullptr;
+ }
+ if (m_rightMenuClickListener != nullptr) {
+ m_rightMenuClickListener->off();
+ delete m_rightMenuClickListener;
+ m_rightMenuClickListener = nullptr;
+ }
+
+ QJsContext *context = qNativeWindowManager->context();
+ if (context) {
+ m_statusManage->call("removeFromStatusBar", {context->object()});
+ }
+ });
+
+ delete m_statusManage;
+ m_statusManage = nullptr;
+ }
+
+ if (m_isInit)
+ g_hasTrayIcon = false;
+
+ m_isInit = false;
+}
+
+void QOhSystemTrayIcon::updateIcon(const QIcon &iconLight, const QIcon &iconDark)
+{
+ if (iconLight.availableSizes().isEmpty() && iconDark.availableSizes().isEmpty()) {
+ qWarning() << "Trayicon available sizes is empty, please check the icon plugin!";
+ return;
+ }
+
+ if (iconLight.isNull() && iconDark.isNull()) {
+ return;
+ }
+
+ m_iconLight = iconLight;
+ m_iconDark = iconDark;
+
+ if (m_iconLight.isNull()) {
+ m_iconLight = m_iconDark;
+ } else if (m_iconDark.isNull()) {
+ m_iconDark = m_iconLight;
+ }
+
+ if (!m_isInit || !m_statusManage)
+ return;
+
+ QtOh::runOnJsUIThreadAndWait([this]{
+ Napi::Object icon = toNativeTrayIcon();
+ if (icon.IsNull() || icon.IsEmpty())
+ return;
+
+ QJsContext *context = qNativeWindowManager->context();
+ if (context) {
+ m_statusManage->call("updateStatusBarIcon", {context->object(), icon});
+
+ if (m_statusManage->hasError()) {
+ qWarning() << "updateStatusBarIcon failed:" << m_statusManage->lastError();
+ }
+ }
+ });
+
+}
+
+void QOhSystemTrayIcon::updateIcon(const QIcon &icon)
+{
+ updateIcon(icon, icon);
+}
+
+void QOhSystemTrayIcon::updateToolTip(const QString &tooltip)
+{
+ //todo:鸿蒙上系统托盘图标暂不支持ToolTip(beta6)
+ m_toolTip = tooltip;
+ if (!tooltip.isEmpty())
+ qWarning() << "Not support to set tray tooltip on openharmony" << this;
+}
+
+QRect QOhSystemTrayIcon::geometry() const
+{
+ //todo:鸿蒙上暂只能获取到height(beta6)
+ QRect result(0,0,0,0);
+ return result;
+}
+
+void QOhSystemTrayIcon::showMessage(const QString &title, const QString &messageIn,
+ const QIcon &icon,
+ QPlatformSystemTrayIcon::MessageIcon iconType,
+ int msecsIn)
+{
+ /* 该接口需要通知权限 */
+ QtOh::runOnJsUIThreadAndWait([&]{
+ QJsModule notificationManager("@ohos.notificationManager");
+
+ static int notificationId = 0;
+ Napi::Object notificationRequest = Napi::Object::New(notificationManager.env());
+ notificationRequest.Set("id", notificationId++);
+ Napi::Object content = Napi::Object::New(notificationManager.env());
+
+ content.Set("notificationContentType", (int)BASIC_TEXT);
+ Napi::Object normal = Napi::Object::New(notificationManager.env());
+ normal.Set("title", title.toStdString());
+ normal.Set("text", messageIn.toStdString());
+ content.Set("normal", normal);
+
+ notificationRequest.Set("content", content);
+ // notificationRequest.Set("notificationSlotType", (int)SERVICE_INFORMATION);
+ QSize validSize(128, 128);
+ if (icon.isNull()) {
+ QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+
+ QPixmap pixmap;
+ switch(iconType) {
+ case MessageIcon::Information:
+ pixmap = theme->standardPixmap(QPlatformTheme::MessageBoxInformation, validSize);
+ break;
+ case MessageIcon::Warning:
+ pixmap = theme->standardPixmap(QPlatformTheme::MessageBoxWarning, validSize);
+ break;
+ case MessageIcon::Critical:
+ pixmap = theme->standardPixmap(QPlatformTheme::MessageBoxCritical, validSize);
+ break;
+ default:
+ break;
+ }
+
+ if (!pixmap.isNull()) {
+ Napi::Value nativePix = pixFromQPixmap(pixmap);
+ notificationRequest.Set("smallIcon", nativePix);
+ notificationRequest.Set("largeIcon", nativePix);
+ }
+ } else {
+ Napi::Value nativePix = pixFromQIcon(icon, validSize);
+ notificationRequest.Set("smallIcon", nativePix);
+ notificationRequest.Set("largeIcon", nativePix);
+ }
+
+ if (msecsIn > 0)
+ notificationRequest.Set("autoDeletedTime", QDateTime::currentMSecsSinceEpoch() + msecsIn);
+
+ Napi::Function callback = Napi::Function::New(QtOh::uiEnv(), [](const Napi::CallbackInfo& info) {
+ if (info.Length() < 1) {
+ LOGW("QSystemTrayIcon::showMessage failed, error unkonwn");
+ return;
+ }
+ Napi::Object error = info[0].As<Napi::Object>();
+ if (error.IsEmpty() || error.IsNull())
+ return;
+
+ Napi::Value code = error.Get("code");
+ if (!code.IsNumber())
+ return;
+
+ if (int errCode = (int)code.ToNumber())
+ LOGW("QSystemTrayIcon::showMessage failed, code is %{public}d, message is %{public}s",\
+ errCode, error.Get("message").ToString().Utf8Value().c_str());
+ });
+
+ notificationManager.call("publish", {notificationRequest, callback});
+ });
+}
+
+bool QOhSystemTrayIcon::isSystemTrayAvailable() const
+{
+ QtOh::Utility::DeviceType type = QtOh::Utility::type();
+ if (type == QtOh::Utility::Tablet && QtOh::apiVersion() >= 20) {
+ return true;
+ }
+
+ return type == QtOh::Utility::PC;
+}
+
+QPlatformMenu *QOhSystemTrayIcon::createMenu() const
+{
+ return new QOhPlatformMenu();
+}
+
+bool QOhSystemTrayIcon::hasTrayIcon()
+{
+ return g_hasTrayIcon || g_tsTryIcon;
+}
+
+Napi::Value QOhSystemTrayIcon::pixFromQIcon(const QIcon &icon, const QSize &size)
+{
+ QSize iconSize = getClosestIconSize(icon, size);
+ QPixmap pixmap = icon.pixmap(iconSize);
+ if (iconSize != size) {
+ pixmap = pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+
+ return pixFromQPixmap(pixmap);
+}
+
+Napi::Value QOhSystemTrayIcon::pixFromQPixmap(const QPixmap &pix)
+{
+ std::unique_ptr<OH_PixelmapNative, decltype(&::OH_PixelmapNative_Release)> nativePixel(QtOh::UdmfHelper::createNativePixelMapFromQImage(pix.toImage()),
+ ::OH_PixelmapNative_Release);
+ napi_value pixelMap { nullptr };
+ int result = ::OH_PixelmapNative_ConvertPixelmapNativeToNapi(QtOh::uiEnv(), nativePixel.get(), &pixelMap);
+ if (Image_ErrorCode::IMAGE_SUCCESS != result) {
+ qWarning("%s convert trayicon pixmap failed. code: %d", Q_FUNC_INFO, result);
+ }
+
+ return Napi::Value::From(QtOh::uiEnv(), pixelMap);
+}
+
+Napi::Array QOhSystemTrayIcon::createOhMenu(QOhPlatformMenu *ohMenu)
+{
+ if (!ohMenu) {
+ return createDefaultMenu();
+ }
+
+ Napi::Array statusBarGroupMenus = Napi::Array::New(QtOh::uiEnv());
+
+ QList<QOhPlatformMenuItem *> actions = ohMenu->childItems();
+ Napi::Array menuItems = Napi::Array::New(QtOh::uiEnv());
+ for (int i = 0; i < actions.size(); ++i) {
+ QOhPlatformMenuItem *item = actions[i];
+ if (!item || !item->isVisible())
+ continue;
+
+ if (item->isSeparator()) {
+ if (menuItems.Length()) {
+ int statusBarCnt = statusBarGroupMenus.Length();
+ statusBarGroupMenus.Set(statusBarCnt, menuItems);
+ menuItems = Napi::Array::New(QtOh::uiEnv());
+ }
+ continue;
+ }
+
+ Napi::Object menuItem = Napi::Object::New(QtOh::uiEnv());
+ menuItem.Set("title", item->text().toStdString());
+
+ if (item->menu()) {
+ Napi::Array subMenus = createSubMenu(qobject_cast<QOhPlatformMenu *>(item->menu()));
+ menuItem.Set("subMenu", subMenus);
+ } else {
+ // QString abilityName = qNativeWindowManager->getAbilityName();
+ /* FIXME 需要点击菜单项不默认弹出窗口,暂时只能给一个错误的值 */
+ QString abilityName = "tempStr";
+ Napi::Object menuAction = Napi::Object::New(QtOh::uiEnv());
+ menuAction.Set("abilityName", abilityName.toStdString());
+ menuAction.Set("notifyOnly", Napi::Boolean::New(QtOh::uiEnv(), true));
+ menuAction.Set("menuCode", QString::number(item->tag(), 16).toStdString());
+
+ menuItem.Set("menuAction", menuAction);
+ }
+
+ int length = menuItems.Length();
+ menuItems.Set(length, menuItem);
+ }
+
+ if (menuItems.Length()) {
+ int statusBarCnt = statusBarGroupMenus.Length();
+ statusBarGroupMenus.Set(statusBarCnt, menuItems);
+ }
+
+ return statusBarGroupMenus;
+}
+
+Napi::Array QOhSystemTrayIcon::createSubMenu(QOhPlatformMenu *ohMenu)
+{
+ Napi::Array menuSubItems = Napi::Array::New(QtOh::uiEnv());
+ if (!ohMenu) {
+ return menuSubItems;
+ }
+
+ QList<QOhPlatformMenuItem *> actions = ohMenu->childItems();
+ for (int i = 0; i < actions.size(); ++i) {
+ QOhPlatformMenuItem *item = actions[i];
+ if (!item || !item->isVisible())
+ continue;
+
+ if (item->menu()) {
+ qWarning() << "Not support adding three-level menus on openharmony!";
+ continue;
+ }
+
+ if (item->isSeparator()) {
+ qWarning() << "Not support adding separator on secondary menus!";
+ continue;
+ }
+
+ Napi::Object menuItem = Napi::Object::New(QtOh::uiEnv());
+ menuItem.Set("subTitle", item->text().toStdString());
+
+ // QString abilityName = qNativeWindowManager->getAbilityName();
+ /* FIXME 需要点击菜单项不默认弹出窗口,暂时只能给一个错误的值 */
+ QString abilityName = "tempStr";
+ Napi::Object menuAction = Napi::Object::New(QtOh::uiEnv());
+ menuAction.Set("abilityName", abilityName.toStdString());
+ menuAction.Set("notifyOnly", Napi::Boolean::New(QtOh::uiEnv(), true));
+ menuAction.Set("menuCode", QString::number(item->tag(), 16).toStdString());
+
+ menuItem.Set("menuAction", menuAction);
+
+ int length = menuSubItems.Length();
+ menuSubItems.Set(length, menuItem);
+ }
+
+ return menuSubItems;
+}
+
+Napi::Array QOhSystemTrayIcon::createDefaultMenu()
+{
+ Napi::Array statusBarGroupMenus = Napi::Array::New(QtOh::uiEnv());
+
+ return statusBarGroupMenus;
+}
+
+Napi::Object QOhSystemTrayIcon::toNativeTrayIcon()
+{
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ if (m_iconDark.isNull() || m_iconLight.isNull())
+ return Napi::Object();
+
+ Napi::Value pixLight = pixFromQIcon(m_iconLight, QSize(24, 24));
+ Napi::Value pixDark = pixFromQIcon(m_iconDark, QSize(24, 24));
+ if (pixLight.IsNull() || pixLight.IsEmpty() || pixDark.IsNull() || pixDark.IsEmpty()) {
+ qWarning() << "Load icon failed!";
+ return Napi::Object();
+ }
+
+ Napi::Object icon = Napi::Object::New(QtOh::uiEnv());
+ icon.Set("white", pixLight);
+ icon.Set("black", pixDark);
+
+ return icon;
+ });
+}
+
+QSize QOhSystemTrayIcon::getClosestIconSize(const QIcon &icon, const QSize &targetSize)
+{
+ QList<QSize> sizes = icon.availableSizes();
+ if (sizes.isEmpty()) {
+ return QSize(); // 没有可用尺寸,返回空 QSize
+ }
+
+ QSize closestSize = sizes.first(); // 先假设第一个是最接近的
+ int minDiff = qAbs(closestSize.width() - targetSize.width()) +
+ qAbs(closestSize.height() - targetSize.height());
+
+ for (const QSize &size : std::as_const(sizes)) {
+ int diff = qAbs(size.width() - targetSize.width()) +
+ qAbs(size.height() - targetSize.height());
+
+ if (diff < minDiff) {
+ closestSize = size;
+ minDiff = diff;
+ }
+ }
+
+ return closestSize;
+}
+
+void QOhSystemTrayIcon::onStatusBarIconClick(const Napi::CallbackInfo &info)
+{
+ if (info.Length() < 1 || !info[0].IsObject()) {
+ return;
+ }
+ Napi::Object eventData = info[0].As<Napi::Object>();
+ if (!eventData.Has("data") || !eventData.Get("data").IsObject()) {
+ return;
+ }
+
+ Napi::Object data = eventData.Get("data").As<Napi::Object>();
+ if (!data.Has("iconClickType") || !data.Get("iconClickType").IsString()) {
+ return;
+ }
+ QString iconClickType = QString::fromStdString(data.Get("iconClickType").ToString());
+ if (iconClickType == "leftClick") {
+ Q_EMIT activated(QPlatformSystemTrayIcon::Trigger);
+ }
+}
+
+void QOhSystemTrayIcon::onRightMenuClick(const Napi::CallbackInfo &info)
+{
+ if (info.Length() < 1 || !info[0].IsObject()) {
+ return;
+ }
+ Napi::Object eventData = info[0].As<Napi::Object>();
+ if (!eventData.Has("data") || !eventData.Get("data").IsObject()) {
+ return;
+ }
+
+ Napi::Object data = eventData.Get("data").As<Napi::Object>();
+ if (!data.Has("menuCode") || !data.Get("menuCode").IsString()) {
+ return;
+ }
+
+ QString menuCode = QString::fromStdString(data.Get("menuCode").ToString());
+ quintptr tag = static_cast<quintptr>(menuCode.toULongLong(nullptr, 16));
+ QPlatformMenuItem *item = m_menu->menuItemForTag(tag);
+ if (item)
+ Q_EMIT item->activated();
+}
+
+void QOhSystemTrayIcon::updateMenu(QPlatformMenu *menu)
+{
+ if (!menu) {
+ m_menu = nullptr;
+ return;
+ }
+
+ QOhPlatformMenu *ohMenu = qobject_cast<QOhPlatformMenu *>(menu);
+ if (m_menu != ohMenu) {
+ disconnect(m_menu, 0, this , 0);
+ m_menu = ohMenu;
+ connect(ohMenu, &QOhPlatformMenu::menuUpdated, this, [this]() { updateMenu(m_menu); });
+ }
+
+ if (m_updateMenuPending == true)
+ return;
+
+ updateMenu_sys(ohMenu);
+}
+
+void QOhSystemTrayIcon::updateMenu_sys(QOhPlatformMenu *ohMenu)
+{
+ static qint64 lastCall = 0;
+ if (QDateTime::currentMSecsSinceEpoch() - lastCall < 20) {
+ qDebug() << "The interface is called too frequently, and the update has been delayed.";
+ m_updateMenuPending = true;
+ QTimer::singleShot(20, this, [this]() { updateMenu_sys(m_menu); });
+ return;
+ }
+
+ m_updateMenuPending = false;
+ if (!m_isInit || !m_statusManage)
+ return;
+
+ lastCall = QDateTime::currentMSecsSinceEpoch();
+ QtOh::runOnJsUIThreadAndWait([this, ohMenu]{
+ Napi::Array menus = createOhMenu(ohMenu);
+
+ QJsContext *context = qNativeWindowManager->context();
+ if (context) {
+ m_statusManage->call("updateStatusBarMenu", {context->object(), menus});
+
+ if (m_statusManage->hasError()) {
+ qWarning() << "updateStatusBarMenu failed:" << m_statusManage->lastError();
+ }
+ }
+ });
+}
+
+Napi::Value tsStatuBarActivate(const Napi::CallbackInfo& info) {
+ Napi::Env env = info.Env();
+ QOhSystemTrayIcon::setTsStatuActivate();
+ return Napi::Boolean::New(env, true);
+}
+
+void QOhSystemTrayIcon::init(Napi::Env env, Napi::Object exports)
+{
+ exports.Set(Napi::String::New(env, "tsStatuBarActivate"), Napi::Function::New(env, tsStatuBarActivate));
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,91 @@
+#ifndef QOHSYSTEMTRAYICON_H
+#define QOHSYSTEMTRAYICON_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qpa/qplatformsystemtrayicon.h>
+#include <QJsObject>
+#include <QJsModule>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+class QOhPlatformMenu;
+class QOhJsOnListener;
+class QOpenHarmonyPopupMenu;
+
+class QOhSystemTrayIcon : public QPlatformSystemTrayIcon
+{
+ Q_OBJECT
+
+ enum ContentType {
+ BASIC_TEXT = 0, /* 普通类型 */
+ LONG_TEXT, /* 长文本类型 */
+ PICTURE, /* 图片类型 */
+ CONVERSATION, /* 不支持 */
+ MULTILINE, /* 多行文本类型 */
+ };
+
+ enum SlotType {
+ UNKNOWN_TYPE = 0, /* 未知类型,LEVEL_MIN,仅通知中心 */
+ SOCIAL_COMMUNICATION, /* 社交通信,LEVEL_HIGH,状态栏显示图标,有横幅提示音 */
+ SERVICE_INFORMATION, /* 服务提醒,LEVEL_HIGH */
+ CONTENT_INFORMATION, /* 内容资讯,LEVEL_MIN*/
+ OTHER_TYPES = 0xFFFF /* 其他, */
+ };
+
+public:
+ QOhSystemTrayIcon();
+ ~QOhSystemTrayIcon() override;
+
+ void init() override;
+ void cleanup() override;
+ void updateIcon(const QIcon &iconLight, const QIcon &iconDark) override;
+ void updateIcon(const QIcon &icon) override;
+ void updateToolTip(const QString &tooltip) override;
+ QRect geometry() const override;
+ void showMessage(const QString &title, const QString &msg,
+ const QIcon &icon, MessageIcon iconType, int msecs) override;
+ bool isSystemTrayAvailable() const override;
+ bool supportsMessages() const override { return true; }
+ QPlatformMenu *createMenu() const override;
+
+ static bool hasTrayIcon();
+ static void setTsStatuActivate(){g_tsTryIcon = true;}
+
+ static void init(Napi::Env env, Napi::Object exports);
+public slots:
+ void updateMenu(QPlatformMenu *) override;
+
+private:
+ Napi::Value pixFromQIcon(const QIcon &icon, const QSize &size);
+ Napi::Value pixFromQPixmap(const QPixmap &pix);
+ Napi::Array createOhMenu(QOhPlatformMenu *);
+ Napi::Array createSubMenu(QOhPlatformMenu *);
+ Napi::Array createDefaultMenu();
+ Napi::Object toNativeTrayIcon();
+ QSize getClosestIconSize(const QIcon &icon, const QSize &size);
+
+ void updateMenu_sys(QOhPlatformMenu *ohMenu);
+
+ void onStatusBarIconClick(const Napi::CallbackInfo &);
+ void onRightMenuClick(const Napi::CallbackInfo &);
+private:
+ QIcon m_iconLight;
+ QIcon m_iconDark;
+ QOhPlatformMenu *m_menu = nullptr;
+ QString m_toolTip;
+
+ bool m_updateMenuPending = false;
+
+ bool m_isInit;
+ static inline bool g_hasTrayIcon = false;
+ static inline bool g_tsTryIcon = false;
+
+ QJsModule *m_statusManage = nullptr;
+ QOhJsOnListener *m_statusBarIconClickListener = nullptr;
+ QOhJsOnListener *m_rightMenuClickListener = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHSYSTEMTRAYICON_H
new file mode 100644
@@ -0,0 +1,319 @@
+#include "qohwindowcontext.h"
+#include "qohplatformscreen.h"
+#include "qohplatformwindow.h"
+#include "qohplatformcursor.h"
+#include "qohplatformopenglwindow.h"
+#include "qohnormalwindow.h"
+#include "qohdisplay.h"
+#include <private/qopenharmony_p.h>
+#include <private/qjspromise_p.h>
+#include <QScreen>
+#include <QJsModule>
+#include <QAccessibleInterface>
+#include <QGuiApplication>
+#include <QRegularExpression>
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+
+QList<QOhPlatformWindow *> QOhWindowContext::allWindows()
+{
+ return m_allWindows;
+}
+
+void QOhWindowContext::setFocusWindow(QWindow *window)
+{
+ if (window == m_focusWindow) {
+ return;
+ }
+ m_focusWindow = window;
+ if (m_focusWindow != nullptr && m_focusWindow->parent() == nullptr)
+ raiseWindow(dynamic_cast<QOhPlatformWindow *>(m_focusWindow->handle()));
+ QWindowSystemInterface::handleWindowActivated(m_focusWindow);
+}
+
+QWindow *QOhWindowContext::focusWindow()
+{
+ return m_focusWindow;
+}
+
+void QOhWindowContext::appendWindow(QOhPlatformWindow *window)
+{
+ if (window == nullptr || m_allWindows.contains(window))
+ return;
+ m_allWindows.prepend(window);
+ m_names << window->windowName();
+}
+
+void QOhWindowContext::removeWindow(QOhPlatformWindow *window)
+{
+ if (window == nullptr || !m_allWindows.contains(window))
+ return;
+ m_allWindows.removeOne(window);
+ m_names.removeOne(window->windowName());
+}
+
+void QOhWindowContext::raiseWindow(QOhPlatformWindow *window)
+{
+ int index = m_allWindows.indexOf(window);
+ if (index <= 0)
+ return;
+ m_allWindows.move(index, 0);
+}
+
+void QOhWindowContext::lowerWindow(QOhPlatformWindow *window)
+{
+ int index = m_allWindows.indexOf(window);
+ if (index == -1 || index == (m_allWindows.size() - 1))
+ return;
+ m_allWindows.move(index, m_allWindows.size() - 1);
+}
+
+QOhPlatformWindow *QOhWindowContext::findWindow(const std::function<bool (QOhPlatformWindow *)> &function)
+{
+ auto it = std::find_if(m_allWindows.constBegin(), m_allWindows.constEnd(), function);
+ return it == m_allWindows.constEnd() ? nullptr : *it;
+}
+
+QOhPlatformWindow *QOhWindowContext::get(QWindow *window)
+{
+ return findWindow([window](QOhPlatformWindow *pw){
+ return pw->window() == window;
+ });
+}
+
+QOhPlatformWindow *QOhWindowContext::get(const QString &name)
+{
+ return findWindow([name](QOhPlatformWindow *pw){
+ return pw->componentName() == name;
+ });
+}
+
+QOhPlatformWindow *QOhWindowContext::getByWindowName(const QString &name)
+{
+ return findWindow([name](QOhPlatformWindow *pw){
+ return pw->windowName() == name;
+ });
+}
+
+QOhPlatformWindow *QOhWindowContext::getByWindowId(int32_t nativeWindowId)
+{
+ return findWindow([nativeWindowId](QOhPlatformWindow *pw){
+ return pw->nativeWindowId() == nativeWindowId;
+ });
+}
+
+QString QOhWindowContext::uniqueName(const QOhPlatformWindow *window)
+{
+ auto qWindow = window->window();
+ QString name = qWindow->objectName();
+
+ static QRegularExpression re("^[a-zA-Z0-9_]+$");
+ if (name.isEmpty() || !re.match(name).hasMatch()) {
+ auto mo = qWindow->metaObject();
+ name = QString::fromLatin1(mo->className()) + "Window";
+ }
+
+ int counter = 1;
+ QString newName;
+ do {
+ newName = QString("%1_%2").arg(name).arg(counter);
+ counter++;
+ } while (m_names.contains(newName));
+
+ return newName;
+}
+
+void QOhWindowContext::setMouseGrabberWindow(QWindow *window)
+{
+ m_mouseGrabber = window;
+ m_underMouseWindow = window;
+}
+
+QWindow *QOhWindowContext::mouseGrabberWindow()
+{
+ return m_mouseGrabber.data();
+}
+
+void QOhWindowContext::setUnderMouseWindow(QPointer<QWindow> window)
+{
+ m_underMouseWindow = window;
+ if (window.isNull() || !m_mouseGrabber.isNull())
+ return;
+ if (QOhPlatformOpenGLWindow *pw = dynamic_cast<QOhPlatformOpenGLWindow *>(window->handle())) {
+ pw->applyCursor();
+ }
+}
+
+void QOhWindowContext::setUnderMouseWindow(QPointer<QWindow> window, const QPoint &local, const QPoint &global)
+{
+ static auto applyCursor = [](QPointer<QWindow> window)
+ {
+ if (window.isNull())
+ return;
+ if (QOhPlatformOpenGLWindow *pw = dynamic_cast<QOhPlatformOpenGLWindow *>(window->handle())) {
+ pw->applyCursor();
+ }
+ };
+
+ static auto handleEnterLeave = [](QPointer<QWindow> window, const QPoint &local, const QPoint &global)
+ {
+ if (m_underMouseWindow != window) {
+ QWindow *leave = m_underMouseWindow.data();
+ m_underMouseWindow = window;
+ QWindowSystemInterface::handleEnterLeaveEvent(window, leave, local, global);
+ applyCursor(window);
+ }
+ };
+
+ static QPointer<QWindow> currentUnderMouseWindow;
+ if (m_mouseGrabber.isNull()) {
+ handleEnterLeave(window, local, global);
+ currentUnderMouseWindow = nullptr;
+ } else {
+ bool showApplyCursor = false;
+ if (currentUnderMouseWindow != window) {
+ currentUnderMouseWindow = window;
+ showApplyCursor = true;
+ }
+
+ if (showApplyCursor && !m_mouseGrabber.isNull() && currentUnderMouseWindow != nullptr) {
+ QWindow *topLevelWindow = topLevelOf(currentUnderMouseWindow);
+ QOhPlatformOpenGLWindow *overrideWindow = dynamic_cast<QOhPlatformOpenGLWindow *>(topLevelWindow->handle());
+ QOhPlatformOpenGLWindow *cursorWindow = dynamic_cast<QOhPlatformOpenGLWindow *>(m_mouseGrabber->handle());
+ if (overrideWindow && cursorWindow) {
+ QOhPlatformCursor::setWindowOverrideCursor(overrideWindow->nativeWindowId(), cursorWindow->cursor());
+ }
+ }
+ }
+}
+
+QWindow *QOhWindowContext::underMouseWindow()
+{
+ return m_underMouseWindow.data();
+}
+
+void QOhWindowContext::setKeyGrabberWindow(QWindow *window)
+{
+ m_keyGrabber = window;
+}
+
+QWindow *QOhWindowContext::keyGrabberWindow()
+{
+ return m_keyGrabber.data();
+}
+
+QWindow *QOhWindowContext::topLevelOf(QWindow *w, QWindow::AncestorMode mode)
+{
+ while (QWindow *parent = w->parent(mode))
+ w = parent;
+ return w;
+}
+
+QWindow *QOhWindowContext::getWindowByCoordinate(QOhPlatformScreen *screen, const QPoint &p)
+{
+ QOhPlatformScreen *_screen = screen;
+ if (_screen == nullptr)
+ _screen = dynamic_cast<QOhPlatformScreen *>(QGuiApplication::primaryScreen()->handle());
+ if (_screen == nullptr)
+ return nullptr;
+
+ /* FIXME wanghao 频繁调用 getWindowsByCoordinate造成卡顿 */
+#if OHOS_SDK_VERSION >= 14
+ QPoint pt = p - _screen->geometry().topLeft();
+ auto id = QtOh::runOnJsUIThreadWithPromise<uint32_t>([&](auto p, uint32_t displayId, const QPoint &pt) {
+ // 查询1个窗口,该窗口为最上层显示的窗口
+ if (m_jsWindowNamespace.isNull())
+ m_jsWindowNamespace.reset(new QJsModule("@ohos.window"));
+
+ Napi::Value result = m_jsWindowNamespace->call("getWindowsByCoordinate", { QNapi::create(displayId),
+ QNapi::create(1),
+ QNapi::create(pt.x()),
+ QNapi::create(pt.y()) });
+ if (result.IsEmpty() || !result.IsPromise()) {
+ p->set_value(-1);
+ return;
+ }
+
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo &info) {
+ if (info.Length() == 0) {
+ p->set_value(-1);
+ return;
+ }
+ auto id = -1;
+ Napi::Array windows = info[0].As<Napi::Array>();
+ if (windows.Length() >= 1) {
+ Napi::Value first = windows[uint32_t(0)];
+ id = QOhNormalWindow::getWindowProperties(first.As<Napi::Object>()).id();
+ }
+ p->set_value(id);
+ }).onCatch([p](const Napi::CallbackInfo &info) {
+ Q_UNUSED(info)
+ p->set_value(-1);
+ });
+ }, _screen->display()->id(), pt);
+
+ if (QOhPlatformWindow *pw = QOhWindowContext::getByWindowId(id)) {
+ QWindow *ret = pw->window();
+ while (ret && !ret->isTopLevel())
+ ret = ret->parent();
+
+ return ret;
+ }
+
+ return nullptr;
+#else
+ return nullptr;
+#endif
+}
+
+QWindow *QOhWindowContext::topLevelAt(QOhPlatformScreen *screen, const QPoint &p)
+{
+ QOhPlatformScreen *_screen = screen;
+ if (_screen == nullptr)
+ _screen = dynamic_cast<QOhPlatformScreen *>(QGuiApplication::primaryScreen()->handle());
+ if (_screen == nullptr)
+ return nullptr;
+ QWindow *ret = nullptr;
+ /* Qt 6 only modify std::as_const */
+ for (QOhPlatformWindow *w : std::as_const(m_allWindows)) {
+ auto window = w->window();
+ if (window->parent())
+ continue;
+ if (w->windowFrameGeometry().contains(p, false) && window->isVisible()) {
+ ret = window;
+ break;
+ }
+ }
+ return ret;
+}
+
+QWindow *QOhWindowContext::windowForInterface(QAccessibleInterface *accessible)
+{
+ QWindow *window = accessible->window();
+ if (!window) {
+ const QAccessibleInterface *acc = accessible;
+ const QAccessibleInterface *par = accessible->parent();
+ while (par && par->isValid() && !window) {
+ window = par->window();
+ acc = par;
+ par = par->parent();
+ }
+ if (!window) {
+ const auto appWindows = QGuiApplication::topLevelWindows();
+ for (QWindow *w : appWindows) {
+ if (QAccessibleInterface *root = w->accessibleRoot()) {
+ int count = root->childCount();
+ for (int i = 0; i < count; ++i) {
+ if (root->child(i) == acc)
+ return w;
+ }
+ }
+ }
+ }
+ }
+ return window;
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,58 @@
+#ifndef QOHWIDOWCONTEXT_H
+#define QOHWIDOWCONTEXT_H
+
+#include <QWindow>
+#include <QList>
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+class QJsModule;
+class QOhPlatformWindow;
+class QOhPlatformScreen;
+
+class QOhWindowContext
+{
+public:
+ static QList<QOhPlatformWindow *> allWindows();
+ static void setFocusWindow(QWindow *window);
+ static QWindow *focusWindow();
+ static void appendWindow(QOhPlatformWindow *window);
+ static void removeWindow(QOhPlatformWindow *window);
+ static void raiseWindow(QOhPlatformWindow *window);
+ static void lowerWindow(QOhPlatformWindow *window);
+
+ static QOhPlatformWindow *get(QWindow *window);
+ static QOhPlatformWindow *get(const QString &name);
+ static QOhPlatformWindow *getByWindowName(const QString &name);
+ static QOhPlatformWindow *getByWindowId(int32_t nativeWindowId);
+ static QString uniqueName(const QOhPlatformWindow *window);
+ static void setMouseGrabberWindow(QWindow *window);
+ static QWindow *mouseGrabberWindow();
+ static void setUnderMouseWindow(QPointer<QWindow> window);
+ static void setUnderMouseWindow(QPointer<QWindow> window, const QPoint &local, const QPoint &global);
+ static QWindow *underMouseWindow();
+ static void setKeyGrabberWindow(QWindow *window);
+ static QWindow *keyGrabberWindow();
+
+ static QWindow *topLevelOf(QWindow *w, QWindow::AncestorMode mode = QWindow::ExcludeTransients);
+
+ static QWindow *getWindowByCoordinate(QOhPlatformScreen *screen, const QPoint &p);
+
+ static QWindow *topLevelAt(QOhPlatformScreen *screen, const QPoint &p);
+
+ static QWindow *windowForInterface(QAccessibleInterface *accessible);
+
+private:
+ static QOhPlatformWindow *findWindow(const std::function<bool(QOhPlatformWindow *)> &function);
+private:
+ static inline QWindow *m_focusWindow = nullptr;
+ static inline QList<QOhPlatformWindow *> m_allWindows = {};
+ static inline QStringList m_names = {};
+ static inline QPointer<QWindow> m_mouseGrabber = {};
+ static inline QPointer<QWindow> m_keyGrabber = {};
+ static inline QPointer<QWindow> m_underMouseWindow = {};
+ static inline QSharedPointer<QJsModule> m_jsWindowNamespace = {};
+};
+
+QT_END_NAMESPACE
+#endif // QOHWIDOWCONTEXT_H
new file mode 100644
@@ -0,0 +1,984 @@
+#include <QUrl>
+#include <QTimer>
+#include <QDebug>
+#include <cstdint>
+#include <qopenharmonydefines.h>
+#include <private/qguiapplication_p.h>
+#include <private/qopenharmony_p.h>
+
+#include "qohmain.h"
+#include "qohdrag.h"
+#include "qohevent.h"
+#include "qohnativewindow.h"
+#include "qohauxiliary.h"
+#include "qohwindownode.h"
+#include "qohxcomponent.h"
+#include "qohplatformscreen.h"
+#include "qohdisplaymanager.h"
+#include "qohplatformwindow.h"
+#include "qohnativenodeapi.h"
+#include "qohplatforminputcontext.h"
+#include "qohwindownodeeventhandler.h"
+#include "qohutility.h"
+
+#include <ace/xcomponent/native_interface_xcomponent.h>
+#include <arkui/drag_and_drop.h>
+
+
+QT_BEGIN_NAMESPACE
+
+#define Z_INDEX_MAX 2147483647
+#define Z_INDEX_SUBNODE_INIT 65536
+
+bool compare(QOhWindowNode *a, QOhWindowNode *b)
+{
+ return a->zIndex() < b->zIndex();
+}
+
+static QList<QOhWindowNode *> g_allNodes = QList<QOhWindowNode *>();
+
+constexpr char *xcIdstr = "window_component_";
+
+static QString XComponentName()
+{
+ static int index = 1;
+ static const QString prefix = QString::fromUtf8(xcIdstr);
+ return prefix + QString::number(index++);
+}
+
+QOhWindowNode::QOhWindowNode(QOhWindowNode *parent)
+ : m_parent(parent)
+ , m_eventHandler(nullptr)
+ , m_window(nullptr)
+ , m_ownerWindow(nullptr)
+ , m_component(nullptr)
+ , m_id(nullptr)
+{
+ g_allNodes << this;
+ QtOh::runOnJsUIThreadAndWait([=]{
+ m_nodeApi = QOhNativeNodeAPI::getOrCreateForNode(this);
+ });
+}
+
+QOhWindowNode::~QOhWindowNode()
+{
+ QtOh::runOnJsUIThreadAndWait([this]{
+ clear();
+ });
+
+ g_allNodes.removeOne(this);
+ for (int i = 0; i < m_deleteChildren.count(); ++i) {
+ auto subNode = m_deleteChildren.at(i);
+ delete subNode;
+ }
+ m_deleteChildren.clear();
+ if (m_component != nullptr) {
+ delete m_component;
+ m_component = nullptr;
+ }
+
+ if (m_eventHandler != nullptr) {
+ delete m_eventHandler;
+ }
+
+ if (m_eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(m_display, m_eglSurface);
+
+ if (m_id != nullptr && m_isIdCreateByQt) {
+ delete m_id;
+ m_id = nullptr;
+ }
+ if (m_parent != nullptr) {
+ m_parent->m_children.removeOne(this);
+ }
+}
+
+bool QOhWindowNode::nodeDestroyed(QOhWindowNode *node)
+{
+ return !g_allNodes.contains(node);
+}
+
+QOhWindowNode *QOhWindowNode::focusNode()
+{
+ for (int i = 0; i < g_allNodes.count(); ++i) {
+ auto node = g_allNodes.at(i);
+ bool f = node->isFocused();
+ if (f) {
+ return node;
+ }
+ }
+ return nullptr;
+}
+
+bool QOhWindowNode::isQtNode(const QString &name)
+{
+ return name.startsWith(QString(xcIdstr));
+}
+
+void QOhWindowNode::attachPlatformWindow(QOhPlatformWindow *window)
+{
+ m_window = window;
+ if (m_eventHandler != nullptr && window != nullptr)
+ m_eventHandler->setTargetWindow(window->window());
+}
+
+void QOhWindowNode::setOwnerWindow(QOhNativeWindow *window)
+{
+ m_ownerWindow = window;
+}
+
+static QOhWindowNode *topNode(QOhWindowNode *node)
+{
+ QOhWindowNode *n = node;
+ while (n->parent() != nullptr) {
+ n = n->parent();
+ }
+ return n;
+}
+
+QOhNativeWindow *QOhWindowNode::ownerWindow() const
+{
+ // 返回node所在的js窗口,子node返回顶层node的window
+ if (m_ownerWindow != nullptr)
+ return m_ownerWindow;
+ QOhWindowNode *top = topNode(const_cast<QOhWindowNode*>(this));
+ return top->m_ownerWindow;
+}
+
+void QOhWindowNode::attachRootComponent(void *component)
+{
+ if (component == nullptr)
+ return;
+
+ m_topComponent = reinterpret_cast<OH_NativeXComponent *>(component);
+ if (m_topComponent != nullptr) {
+ QtOh::runOnJsUIThreadNoWait([this]{
+ if (m_ownerWindow && m_ownerWindow->isDestroyed()) {
+ qWarning() << "attachRootComponent failed , native window is destroyed!";
+ return;
+ }
+
+ int32_t result = OH_NativeXComponent_AttachNativeRootNode(m_topComponent, m_stack);
+ if (result != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("%{public}s attach root node failed: %{public}d", qPrintable(componentName()), result);
+ } else {
+ move(0, 0);
+ setSizeAdaptive();
+ LOGI("%{public}s attach root node success", qPrintable(componentName()));
+ }
+ });
+ }
+}
+
+void QOhWindowNode::detachRootComponent(bool isDestroy)
+{
+ if (m_topComponent != nullptr) {
+ QtOh::runOnJsUIThreadAndWait([this]{
+ if (m_ownerWindow && m_ownerWindow->isDestroyed())
+ return;
+
+ int32_t result = OH_NativeXComponent_DetachNativeRootNode(m_topComponent, m_stack);
+ if (result != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("%{public}s detach root node failed: %{public}d", qPrintable(componentName()), result);
+ } else {
+ LOGI("%{public}s detach root node success", qPrintable(componentName()));
+ }
+ });
+
+ if (isDestroy)
+ m_topComponent = nullptr;
+ }
+}
+
+QString QOhWindowNode::componentName() const
+{
+ if (m_component == nullptr)
+ return QString();
+ return m_component->name();
+}
+
+void QOhWindowNode::setComponent(QOhXComponent *component)
+{
+ if (component == nullptr)
+ return;
+ m_component = component;
+}
+
+void QOhWindowNode::setNativeWindow(void *nativeWindow)
+{
+ m_nativeWindow = nativeWindow;
+ if (m_nativeWindow != nullptr) {
+ m_nativeWindowPromise.set_value();
+ }
+ if (m_id) {
+ WindowNode *wNode = reinterpret_cast<WindowNode *>(m_id);
+ wNode->window = nativeWindow;
+ }
+}
+
+OH_NativeXComponent *QOhWindowNode::component() const
+{
+ if (m_component == nullptr)
+ return nullptr;
+ return m_component->nativeComponent();
+}
+
+EGLSurface QOhWindowNode::eglSurface(EGLDisplay display, EGLConfig config)
+{
+ m_display = display;
+ if (m_nativeWindow == nullptr) {
+ return m_eglSurface;
+ }
+
+ if (m_eglSurface == EGL_NO_SURFACE) {
+ /*openharmony 上该段代码会导致eglSwapBuffers执行报错:EGL_BAD_ALLOC*/
+ if (!QtOh::isOpenHarmonyDevice()) {
+ EGLint native_visual_id(0);
+ if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
+ LOGE("eglGetConfigAttrib EGL_NATIVE_VISUAL_ID failed: [%{public}d]", eglGetError());
+ }
+ /* FIXME qml 弹窗黑屏 */
+ if (auto nativeWindow = reinterpret_cast<OHNativeWindow*>(m_nativeWindow)) {
+ int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, SET_FORMAT, native_visual_id);
+ LOGI("call OH_NativeWindow_NativeWindowHandleOpt status: [%{public}d]", ret);
+ }
+ }
+
+ m_eglSurface = eglCreateWindowSurface(display,
+ config,
+ reinterpret_cast<EGLNativeWindowType>(m_nativeWindow), nullptr);
+ if (m_eglSurface == nullptr) {
+ LOGE("eglCreateWindowSurface: unable to create surface %{public}d", eglGetError());
+ }
+ }
+
+ return m_eglSurface;
+}
+
+QOhWindowNode *QOhWindowNode::parent() const
+{
+ return m_parent;
+}
+
+void QOhWindowNode::setParent(QOhWindowNode *parent)
+{
+ if (m_xcomponent == nullptr || m_stack == nullptr)
+ return;
+
+ QtOh::runOnJsUIThreadAndWait([&]{
+ if (m_parent == parent)
+ return;
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::removeChild) || !m_nodeApi->has(&ArkUI_NativeNodeAPI_1::addChild)) {
+ LOGE("setParent nodeAPI nullptr error");
+ return;
+ }
+
+ if (m_stack == nullptr) {
+ LOGE("setParent stack nullptr error");
+ return;
+ }
+
+ if (m_parent != nullptr && m_parent->m_stack != nullptr) {
+ m_nodeApi->removeChild(m_parent->m_stack, m_stack);
+ m_parent->m_children.removeOne(this);
+ } else {
+ LOGI("setParent the old parent do not need removeChild");
+ }
+
+ if (parent != nullptr) {
+ if (m_topComponent != nullptr) {
+ detachRootComponent();
+ }
+ auto newParentStack = parent->m_stack;
+ if (newParentStack != nullptr) {
+ m_nodeApi->addChild(newParentStack, m_stack);
+ } else {
+ LOGW("setParent the new parent addChild error");
+ }
+ parent->m_children << this;
+ } else if (m_parent != nullptr && m_topComponent) {
+ /*之前是rootNode,然后setParent(A),再次setParent(null)时,需要重新attach*/
+ attachRootComponent(m_topComponent);
+ }
+ m_parent = parent;
+ });
+}
+
+QOhWindowNode *QOhWindowNode::fromId(WId id)
+{
+ Node *node = reinterpret_cast<Node *>(id);
+ if (node == nullptr)
+ return nullptr;
+ if (std::strcmp(node->nodeOwner, "Qt") == 0) {
+ QOhWindowNode *wn = reinterpret_cast<QOhWindowNode *>(node->nodePrivate);
+ return wn;
+ } else {
+ for (int i = 0; i < g_allNodes.count(); ++i) {
+ auto temp = g_allNodes.at(i);
+ if (temp->m_stack == node->container && temp->m_xcomponent == node->node) {
+ if (temp->m_id != node) {
+ if (temp->m_isIdCreateByQt) {
+ delete temp->m_id;
+ }
+ temp->m_id = node;
+ temp->m_isIdCreateByQt = (std::strcmp(node->nodeOwner, "QtInner") == 0);
+ }
+ return temp;
+ }
+ }
+ QOhWindowNode *wn = new QOhWindowNode;
+ wn->m_stack = node->container;
+ wn->m_xcomponent = node->node;
+ wn->m_id = node;
+ wn->m_isExternal = true;
+ wn->m_isIdCreateByQt = (std::strcmp(node->nodeOwner, "QtInner") == 0);
+ auto api = wn->nodeApi();
+ api->attach(wn->m_stack);
+ api->attach<QOhNativeNodeAPI::XCompoentType>(wn->m_xcomponent);
+ return QtOh::runOnJsUIThreadWithResult([=]{
+ if (wn->m_stack == nullptr) {
+ wn->m_stack = api->createNode(ARKUI_NODE_STACK);
+ api->setAttribute(NODE_ACCESSIBILITY_ROLE, (uint32_t)ARKUI_NODE_STACK);
+ api->setAttribute(NODE_STACK_ALIGN_CONTENT, ARKUI_ALIGNMENT_TOP_START);
+ // set NODE_Z_INDEX
+ api->setAttribute(NODE_Z_INDEX, Z_INDEX_SUBNODE_INIT);
+ node->container = wn->m_stack;
+ api->addChild(wn->m_stack, wn->m_xcomponent);
+ }
+ auto nativeParentStack = api->parent(wn->m_stack);
+ if (nativeParentStack != nullptr) {
+ for (int i = 0; i < g_allNodes.count(); ++i) {
+ auto p = g_allNodes.at(i);
+ if (nativeParentStack == p->m_stack) {
+ wn->m_parent = p;
+ p->m_children << wn;
+ }
+ }
+ }
+ api->setLengthMetricUnit(ARKUI_LENGTH_METRIC_UNIT_PX);
+ wn->createNodeEventHandler();
+ wn->m_eventHandler->registerEvents({NODE_ON_MOUSE, NODE_ON_HOVER});
+ return wn;
+ });
+ }
+ // Todo other framework id
+ return nullptr;
+}
+
+void QOhWindowNode::setGeometry(const QRect &rect)
+{
+ if (m_parent == nullptr || m_window == nullptr)
+ return;
+ QRectF r(rect);
+ QtOh::runOnJsUIThreadAndWait([this, r]{
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::setAttribute)) {
+ LOGE("set geometry node nodeAPI nullptr error");
+ return;
+ }
+
+ m_nodeApi->setAttribute(NODE_WIDTH, static_cast<float>(r.width()));
+ m_nodeApi->setAttribute(NODE_HEIGHT, static_cast<float>(r.height()));
+ m_nodeApi->setAttribute(NODE_POSITION, static_cast<float>(r.x()), static_cast<float>(r.y()));
+ //m_nodeApi->setAttribute(NODE_XCOMPONENT_SURFACE_SIZE, static_cast<uint32_t>(r.width()), static_cast<uint32_t>(r.height()));
+#if OHOS_SDK_VERSION >= 18
+ m_nodeApi->setAttribute(NODE_XCOMPONENT_SURFACE_RECT, static_cast<int32_t>(r.x()), static_cast<int32_t>(r.y()),
+ static_cast<int32_t>(r.width()), static_cast<int32_t>(r.height()));
+#endif
+ });
+}
+
+void QOhWindowNode::handleSurfaceChanged(const QSize &size)
+{
+ Q_UNUSED(size)
+ if (m_window == nullptr || m_parent != nullptr || m_ownerWindow == nullptr)
+ return;
+
+ // 如果QWidget主动设置大小,reportWindowGeometry不会导致窗口重新绘制,而Qt在主动设置大小之后绘制时,
+ // 由于异步系统窗口大小还未正确改变成设置的大小,导致绘制不完整,如果大小和Qt窗口大小一直,强制绘制。
+ QRect nativeRect = m_ownerWindow->geometry();
+ QMargins m = m_ownerWindow->frameMargins();
+ nativeRect -= m;
+ QRect qtRect = m_window->geometry();
+ if (qtRect.size() == nativeRect.size()) {
+ m_window->fireFullExpose();
+ }
+ m_ownerWindow->reportWindowGeometry();
+}
+
+void QOhWindowNode::setVisible(bool visible)
+{
+ if (visible && m_isExternal)
+ handleSubNodes(this);
+
+ if (m_stack == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&]{
+ m_nodeApi->setAttribute(NODE_VISIBILITY, visible ? ARKUI_VISIBILITY_VISIBLE : ARKUI_VISIBILITY_HIDDEN);
+ });
+}
+
+void QOhWindowNode::setNodeFocus(bool isFocus)
+{
+ if (m_xcomponent == nullptr)
+ return;
+
+ QtOh::runOnJsUIThreadAndWait([this, isFocus]{
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUS_STATUS, isFocus);
+ });
+}
+
+bool QOhWindowNode::isFocused() const
+{
+ if (m_xcomponent == nullptr)
+ return false;
+
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ const ArkUI_AttributeItem *item = m_nodeApi->getAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUS_STATUS);
+ return item == nullptr ? false : item->value[0].i32 == 1;
+ });
+}
+
+bool QOhWindowNode::isVisible() const
+{
+ if (m_stack == nullptr)
+ return false;
+
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ const ArkUI_AttributeItem *item = m_nodeApi->getAttribute(NODE_VISIBILITY);
+ return item == nullptr ? false : item->value[0].i32 == ARKUI_VISIBILITY_VISIBLE;
+ });
+}
+
+void QOhWindowNode::setSizeAdaptive()
+{
+ QtOh::runOnJsUIThreadAndWait([&]{
+ // set stack scale percent
+ m_nodeApi->setAttribute(NODE_WIDTH_PERCENT, 1.0f);
+ m_nodeApi->setAttribute(NODE_HEIGHT_PERCENT, 1.0f);
+ });
+}
+
+void QOhWindowNode::handleSubNodes(QOhWindowNode *parent)
+{
+ if (parent == nullptr || !QOhNativeNodeAPI::has(&ArkUI_NativeNodeAPI_1::getTotalChildCount)
+ || !QOhNativeNodeAPI::has(&ArkUI_NativeNodeAPI_1::getChildAt))
+ return;
+
+ auto nodeAPI = QOhNativeNodeAPI::nativeNodeAPI();
+ auto stack = parent->m_stack;
+ auto subCount = nodeAPI->getTotalChildCount(stack);
+ if (subCount <= 1)
+ return;
+
+ for (uint i = 1; i < subCount; i++) {
+ auto handle = nodeAPI->getChildAt(stack, i);
+ if (!handle)
+ continue;
+ if (OH_ArkUI_NodeUtils_GetNodeType(handle) != ARKUI_NODE_STACK)
+ continue;
+
+ auto count = nodeAPI->getTotalChildCount(handle);
+ if (count == 0)
+ continue;
+
+ auto xcomponent = nodeAPI->getChildAt(handle, 0);
+ if (!xcomponent)
+ continue;
+ if (exist(handle, xcomponent))
+ continue;
+
+ Node *id = new Node;
+ id->nodeType = ARKUI_NODE_XCOMPONENT;
+ id->node = handle;
+ id->container = xcomponent;
+ memset(id->nodeOwner, 0, 8);
+ // Qt internal
+ strcpy(id->nodeOwner, "QtInner");
+ auto wn = QOhWindowNode::fromId(reinterpret_cast<WId>(id));
+ parent->m_deleteChildren << wn;
+ handleSubNodes(wn);
+ }
+}
+
+bool QOhWindowNode::exist(ArkUI_NodeHandle stack, ArkUI_NodeHandle xcomponent)
+{
+ for (int i = 0; i < g_allNodes.count(); ++i) {
+ auto node = g_allNodes.at(i);
+ if (node->m_stack == stack && node->m_xcomponent == xcomponent)
+ return true;
+ }
+ return false;
+}
+
+WId QOhWindowNode::id() const
+{
+ WId ret = reinterpret_cast<WId>(m_id);
+ return ret;
+}
+
+OH_PixelmapNative *QOhWindowNode::grab()
+{
+#if OHOS_SDK_VERSION >= 15
+ if (m_stack == nullptr)
+ return nullptr;
+ auto option = OH_ArkUI_CreateSnapshotOptions();
+ OH_ArkUI_SnapshotOptions_SetScale(option, 1.0f);
+ OH_PixelmapNative *result = nullptr;
+ OH_ArkUI_GetNodeSnapshot(m_stack, option, &result);
+ OH_ArkUI_DestroySnapshotOptions(option);
+ return result;
+#else
+ if (m_nativeWindow == nullptr)
+ return nullptr;
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ uint64_t surfaceId = 0;
+ OH_NativeWindow_GetSurfaceId(reinterpret_cast<OHNativeWindow *>(m_nativeWindow), &surfaceId);
+ static QJsModule image("@ohos.multimedia.image");
+ Napi::Value pixelMap = image.call("createPixelMapFromSurfaceSync", {Napi::String::New(image.env(), QString::number(surfaceId).toStdString())});
+ OH_PixelmapNative *result = nullptr;
+ OH_PixelmapNative_ConvertPixelmapNativeFromNapi(image.env(), pixelMap, &result);
+ return result;
+ });
+#endif
+}
+
+void QOhWindowNode::raise()
+{
+ if (m_stack == nullptr || m_parent == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&]{
+ if (m_parent == nullptr) {
+ LOGE("raise node parent nullptr error");
+ return;
+ }
+ int32_t newZIndex = subNodeMaxZIndex(m_parent);
+ if (newZIndex == m_zIndex) {
+ LOGI("node already on the top, index: %{public}d", newZIndex);
+ return;
+ }
+ if (newZIndex >= Z_INDEX_MAX) {
+ LOGE("raise node index invalid, index: %{public}d", newZIndex);
+ return;
+ }
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::setAttribute)) {
+ LOGE("raise nodeAPI nullptr error");
+ return;
+ }
+ newZIndex++;
+ m_nodeApi->setAttribute(NODE_Z_INDEX, newZIndex);
+ m_zIndex = newZIndex;
+ LOGI("raise node newZIndex = %{public}d", newZIndex);
+ });
+}
+
+void QOhWindowNode::lower()
+{
+ if (m_stack == nullptr || m_parent == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&]{
+ if (m_parent == nullptr) {
+ LOGE("lower node parent nullptr error");
+ return;
+ }
+ int32_t newZIndex = subNodeMinZIndex(m_parent);
+ if (newZIndex == m_zIndex) {
+ LOGI("lower node already at the bottom, index: %{public}d", newZIndex);
+ return;
+ }
+ if (newZIndex <= 1) {
+ LOGE("lower node node index error, index: %{public}d", newZIndex);
+ return;
+ }
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::setAttribute)) {
+ LOGE("lower nodeAPI nullptr error");
+ return;
+ }
+ newZIndex = newZIndex - 1;
+ m_nodeApi->setAttribute(NODE_Z_INDEX, newZIndex);
+ m_zIndex = newZIndex;
+ LOGI("lowerEWNode lowerNode = %{public}d", newZIndex);
+ });
+}
+
+void QOhWindowNode::move(int x, int y)
+{
+ if (m_stack == nullptr)
+ return;
+ QtOh::runOnJsUIThreadAndWait([&]{
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::setAttribute)) {
+ LOGE("move nodeAPI nullptr error");
+ return;
+ }
+
+ m_nodeApi->setAttribute(NODE_POSITION, static_cast<float>(x), static_cast<float>(y));
+ });
+}
+
+QRectF QOhWindowNode::geometry() const
+{
+ if (m_stack == nullptr)
+ return QRectF();
+ return QtOh::runOnJsUIThreadWithResult([this]{
+ float x = 0;
+ float y = 0;
+ float w = 0;
+ float h = 0;
+ const ArkUI_AttributeItem *item = m_nodeApi->getAttribute(NODE_WIDTH); // NODE_WIDTH_PERCENT
+ if (item != nullptr && item->size == 1)
+ w = item->value[0].f32;
+ else {
+ item = m_nodeApi->getAttribute(NODE_WIDTH_PERCENT);
+ if (item != nullptr && item->size == 1)
+ w = item->value[0].f32;
+ }
+ item = m_nodeApi->getAttribute(NODE_HEIGHT);
+ if (item != nullptr && item->size == 1)
+ h = item->value[0].f32;
+ else {
+ item = m_nodeApi->getAttribute(NODE_HEIGHT_PERCENT);
+ if (item != nullptr && item->size == 1)
+ h = item->value[0].f32;
+ }
+ item = m_nodeApi->getAttribute(NODE_POSITION);
+ if (item != nullptr && item->size == 2) {
+ x = item->value[0].f32;
+ y = item->value[1].f32;
+ }
+ return QRect(x, y, w, h);
+ });
+}
+
+QSize QOhWindowNode::surfaceSize() const
+{
+ if (m_component == nullptr)
+ return QSize();
+
+ uint64_t w;
+ uint64_t h;
+ if (void *nativeW = reinterpret_cast<void*>(m_nativeWindow)) {
+ int32_t ret = OH_NativeXComponent_GetXComponentSize(m_component->nativeComponent(), nativeW, &w, &h);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("Get surface size failed");
+ return QSize();
+ }
+ return QSize(w, h);
+ }
+ return QSize();
+}
+
+QColor QOhWindowNode::backgroundColor() const
+{
+ if (m_xcomponent == nullptr)
+ return QColor();
+
+ return QtOh::runOnJsUIThreadWithResult([&]{
+ const ArkUI_AttributeItem *item = m_nodeApi->getAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_BACKGROUND_COLOR);
+ if (nullptr == item)
+ return QColor();
+
+ return QColor::fromRgba(item->value[0].u32);
+ });
+}
+
+void QOhWindowNode::setBackgroundColor(const QColor &color)
+{
+ if (m_xcomponent == nullptr)
+ return;
+
+
+ qInfo() << "set xc background-color:"<< color << QString::number(color.rgba(), 16);
+
+ QtOh::runOnJsUIThreadAndWait([&]{
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_BACKGROUND_COLOR, (uint32_t)color.rgba());
+ });
+}
+
+void QOhWindowNode::setWindowFlags(Qt::WindowFlags flags)
+{
+ if (flags.testFlag(Qt::WindowStaysOnTopHint)) {
+ m_zIndex = INT_MAX/2;
+ /* TODO up zIndex */
+ }
+
+ if (flags.testFlag(Qt::WindowStaysOnBottomHint)) {
+ m_zIndex = 0;
+ /* TODO down zIndex */
+ }
+ /* TODO other flags */
+}
+
+QOhPlatformWindow *QOhWindowNode::platformWindow() const
+{
+ return m_window;
+}
+
+bool QOhWindowNode::isExternal() const
+{
+ return m_isExternal;
+}
+
+int32_t QOhWindowNode::zIndex() const
+{
+ return m_zIndex;
+}
+
+void QOhWindowNode::createOSRootNode(const QString &id, const QRectF &rect, bool scale)
+{
+ createOSNode(nullptr, id, rect, scale);
+}
+
+void QOhWindowNode::createOSChildNode(QOhWindowNode *parentNode, const QString &id, const QRectF &rect, bool scale)
+{
+ createOSNode(parentNode, id, rect, scale);
+}
+
+void QOhWindowNode::createOSNode(QOhWindowNode *parentNode, const QString &id, const QRectF &rect, bool scale)
+{
+ QtOh::runOnJsUIThreadAndWait([=]{
+ LOGI("createEWNode enter");
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::createNode) || !m_nodeApi->has(&ArkUI_NativeNodeAPI_1::addChild)
+ || !m_nodeApi->has(&ArkUI_NativeNodeAPI_1::setAttribute) || !m_nodeApi->has(&ArkUI_NativeNodeAPI_1::disposeNode)) {
+ LOGE("native node api function nullptr error!");
+ return;
+ }
+ ArkUI_NodeHandle parentStack = nullptr;
+ int32_t curZINdex = Z_INDEX_SUBNODE_INIT;
+ if (parentNode != nullptr) {
+ LOGI("createNode: parentNode exits");
+ parentStack = parentNode->m_stack;
+ curZINdex = subNodeMaxZIndex(parentNode) + 1;
+ LOGI("createNode: curZINdex = %{public}d", curZINdex);
+ }
+
+ /* create stack */
+ ArkUI_NodeHandle stack = m_nodeApi->createNode(ARKUI_NODE_STACK);
+ if (stack == nullptr) {
+ LOGE("createNode: create stack nullptr error!");
+ return;
+ }
+
+ /* set metric unit */
+ m_nodeApi->setLengthMetricUnit(ARKUI_LENGTH_METRIC_UNIT_PX);
+ if (scale) {
+ /* set stack scale percent */
+ m_nodeApi->setAttribute(NODE_WIDTH_PERCENT, static_cast<float>(rect.width()));
+ m_nodeApi->setAttribute(NODE_HEIGHT_PERCENT, static_cast<float>(rect.height()));
+ } else {
+ /* set stack size */
+ m_nodeApi->setAttribute(NODE_WIDTH, static_cast<float>(rect.width()));
+ m_nodeApi->setAttribute(NODE_HEIGHT, static_cast<float>(rect.height()));
+ }
+
+ /* set NODE_STACK_ALIGN_CONTENT */
+ m_nodeApi->setAttribute(NODE_STACK_ALIGN_CONTENT, ARKUI_ALIGNMENT_TOP_START);
+ /* set NODE_Z_INDEX */
+ m_nodeApi->setAttribute(NODE_Z_INDEX, curZINdex);
+ /* set stack position */
+ m_nodeApi->setAttribute(NODE_POSITION, static_cast<float>(rect.x()), static_cast<float>(rect.y()));
+ /* set node clip */
+ m_nodeApi->setAttribute(NODE_CLIP, 1);
+
+ /* create xcomponent */
+ ArkUI_NodeHandle xc = m_nodeApi->createNode(ARKUI_NODE_XCOMPONENT);
+ if (xc == nullptr) {
+ m_nodeApi->disposeNode();
+ LOGE("createNode: createNode type ARKUI_NODE_XCOMPONENT failed!");
+ return;
+ }
+
+ /* set node id */
+ QByteArray idArray = id.toUtf8();
+ const char *_id = idArray.constData();
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_ID, _id);
+ /* set xcomponent id */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_XCOMPONENT_ID, _id);
+ /* set xcomponent type */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_XCOMPONENT_TYPE, ARKUI_XCOMPONENT_TYPE_SURFACE);
+ /* set xcomponent focusable */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUSABLE, 1);
+ /* set xcomponent focus on touch */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUS_ON_TOUCH, 1);
+ /* set node render fit */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_RENDER_FIT, ARKUI_RENDER_FIT_TOP_LEFT);
+ /* set NODE_FOCUS_STATUS */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUS_STATUS, 1);
+ /* set xcompoent Z Index */
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_Z_INDEX, 1);
+ m_nodeApi->addChild(stack, xc);
+ m_stack = stack;
+ m_xcomponent = xc;
+ m_zIndex = curZINdex;
+
+ if (parentStack != nullptr) {
+ m_nodeApi->addChild(parentStack, stack);
+ }
+
+ if (parentNode != nullptr) {
+ parentNode->m_children << this;
+ }
+ LOGI("createNode end");
+ });
+}
+
+void QOhWindowNode::createNodeEventHandler()
+{
+ m_eventHandler = new QOhWindowNodeEventHandler(this);
+ if (m_window != nullptr)
+ m_eventHandler->setTargetWindow(m_window->window());
+}
+
+void QOhWindowNode::setFocusOnTouch(bool focusOnTouch)
+{
+ QtOh::runOnJsUIThreadAndWait([&]{
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_FOCUS_ON_TOUCH, focusOnTouch);
+ });
+}
+
+int32_t QOhWindowNode::subNodeMaxZIndex(QOhWindowNode *node)
+{
+ if (node == nullptr) {
+ LOGW("getSubNodeMaxZIndex nullptr error");
+ return Z_INDEX_SUBNODE_INIT;
+ }
+ auto childList = node->m_children;
+ std::sort(childList.begin(), childList.end(), compare);
+
+ return childList.size() > 0 ? childList.back()->zIndex() : Z_INDEX_SUBNODE_INIT;
+}
+
+int32_t QOhWindowNode::subNodeMinZIndex(QOhWindowNode *node)
+{
+ if (node == nullptr) {
+ LOGW("getSubNodeMinZIndex nullptr error");
+ return 0;
+ }
+ auto childList = node->m_children;
+ std::sort(childList.begin(), childList.end(), compare);
+ return childList.size() > 0 ? childList.front()->zIndex() : Z_INDEX_SUBNODE_INIT;
+}
+
+QSharedPointer<QOhNativeNodeAPI> QOhWindowNode::nodeApi() const
+{
+ return m_nodeApi;
+}
+
+#if QT_CONFIG(vulkan)
+#include <qohplatformvulkaninstance.h>
+VkSurfaceKHR *QOhWindowNode::vkSurface() const
+{
+ if (m_vkSurface != VK_NULL_HANDLE) {
+ return &m_vkSurface;
+ }
+ if (m_window == nullptr)
+ return nullptr;
+ if (m_window->window()->surfaceType() == QSurface::VulkanSurface) {
+ QVulkanInstance *inst = m_window->window()->vulkanInstance();
+ if (inst)
+ m_vkSurface = static_cast<QOhPlatformVulkanInstance *>(inst->handle())->createSurface(m_nativeWindow);
+ else
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ return &m_vkSurface;
+ }
+ return nullptr;
+}
+#endif
+
+void QOhWindowNode::setRenderFit(ArkUI_RenderFit fit)
+{
+ if (m_nodeApi)
+ m_nodeApi->setAttribute<QOhNativeNodeAPI::XCompoentType>(NODE_RENDER_FIT, fit);
+}
+
+void QOhWindowNode::clear()
+{
+ if (m_xcomponent == nullptr || m_stack == nullptr)
+ return;
+
+ m_eventHandler->unregisterEvents();
+ if (!m_nodeApi->has(&ArkUI_NativeNodeAPI_1::removeChild)) {
+ LOGE("remove child node function nullptr error");
+ return;
+ }
+ if (m_parent != nullptr) {
+ m_parent->m_children.removeOne(this);
+ auto parentStack = m_parent->m_stack;
+ if (parentStack != nullptr && m_stack != nullptr) {
+ m_nodeApi->removeChild(parentStack, m_stack);
+ }
+ }
+ if (!m_isExternal) {
+ if (m_stack != nullptr && m_xcomponent != nullptr) {
+ m_nodeApi->removeChild(m_stack, m_xcomponent);
+ }
+ if (m_stack != nullptr) {
+ m_nodeApi->disposeNode();
+ }
+ if (m_xcomponent != nullptr) {
+ m_nodeApi->disposeNode<QOhNativeNodeAPI::XCompoentType>();
+ }
+ }
+}
+
+void QOhWindowNode::createNode(const QRectF &rect, bool isFocusable)
+{
+ auto feature = m_nativeWindowPromise.get_future();
+ WindowNode *id = new WindowNode;
+ m_id = id;
+ id->nodeType = ARKUI_NODE_XCOMPONENT;
+ id->node = nullptr;
+ id->container = nullptr;
+ memset(id->nodeOwner, 0, 8);
+ strcpy(id->nodeOwner, "Qt");
+ id->nodePrivate = this;
+ id->window = nullptr;
+ QtOh::runOnJsUIThreadAndWait([this, rect, isFocusable]{
+ bool root = m_parent == nullptr;
+ if (root) {
+ QRectF r = QRectF(0, 0, 1.0f, 1.0f);
+ createOSRootNode(XComponentName(), r, root);
+ } else {
+ createOSChildNode(m_parent, XComponentName(), rect);
+ }
+
+ auto *nativeXComponent = OH_NativeXComponent_GetNativeXComponent(m_xcomponent);
+ QOhXComponent *component = new QOhXComponent(this, nativeXComponent);
+ setComponent(component);
+ setVisible(false);
+
+ /* 从cef窗口切换到同级的qt窗口输入框时需要获取焦点 */
+ setFocusOnTouch(isFocusable);
+ OH_ArkUI_SetNodeDraggable(m_xcomponent, false);
+ std::vector<ArkUI_NodeEventType> events = {NODE_ON_BLUR, NODE_ON_FOCUS, NODE_ON_HOVER, NODE_ON_MOUSE};
+#if OHOS_SDK_VERSION > 14
+ events.push_back(NODE_TOUCH_EVENT);
+#endif
+#if QT_CONFIG(draganddrop)
+ OH_ArkUI_AllowNodeAllDropDataTypes(m_xcomponent);
+ events.push_back(NODE_ON_DRAG_ENTER);
+ events.push_back(NODE_ON_DRAG_MOVE);
+ events.push_back(NODE_ON_DRAG_LEAVE);
+ events.push_back(NODE_ON_DROP);
+#endif
+#if OHOS_SDK_VERSION >= 17
+ events.push_back(NODE_ON_AXIS);
+ events.push_back(NODE_ON_KEY_EVENT);
+#endif
+ createNodeEventHandler();
+ m_eventHandler->registerEvents(events);
+ });
+ id->node = m_xcomponent;
+ id->container = m_stack;
+ const QWindowList &wl = qApp->topLevelWindows();
+ bool allInvisible = std::all_of(wl.begin(), wl.end(), [](const QWindow *obj) { return !obj->isVisible(); });
+
+ static bool isFirstWindow = true;
+ if (!allInvisible || isFirstWindow) { /* 窗口全部隐藏时onSurfaceCreated无法触发 */
+ isFirstWindow = false;
+ auto status = feature.wait_for(std::chrono::seconds(1));
+ if (status == std::future_status::timeout) {
+ qWarning() << "wait for native window handle time out";
+ }
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,121 @@
+#ifndef QOHWINDOWNODE_H
+#define QOHWINDOWNODE_H
+
+#include <EGL/egl.h>
+#include <QRect>
+#include <QtGui/qwindowdefs.h>
+#include <qpa/qplatformdrag.h>
+#include <napi/native_api.h>
+#include <arkui/native_node.h>
+#include <arkui/native_interface.h>
+#include <native_window/external_window.h>
+#include <future>
+#include <QPointer>
+#if QT_CONFIG(vulkan)
+#include <vulkan/vulkan.h>
+#endif
+
+struct OH_NativeXComponent;
+struct Node;
+struct OH_PixelmapNative;
+QT_BEGIN_NAMESPACE
+class QOhNativeWindow;
+class QOhXComponent;
+class QOhPlatformWindow;
+class QOhWindowNodeEventHandler;
+class QOhNativeNodeAPI;
+
+class QOhWindowNode
+{
+public:
+ explicit QOhWindowNode(QOhWindowNode *parent = nullptr);
+ virtual ~QOhWindowNode();
+
+ static bool nodeDestroyed(QOhWindowNode *node);
+ static QOhWindowNode *focusNode();
+ static bool isQtNode(const QString &name);
+ void setOwnerWindow(QOhNativeWindow *window);
+ QOhNativeWindow *ownerWindow() const;
+ void attachPlatformWindow(QOhPlatformWindow *window);
+ void attachRootComponent(void *component);
+ void detachRootComponent(bool isDestroy = false);
+ QString componentName() const;
+
+ void handleSurfaceChanged(const QSize &size);
+ void setComponent(QOhXComponent *component);
+ void setNativeWindow(void *nativeWindow);
+
+ OH_NativeXComponent *component() const;
+ EGLSurface eglSurface(EGLDisplay display, EGLConfig config);
+
+ QOhWindowNode *parent() const;
+ void setParent(QOhWindowNode *parent);
+
+ static QOhWindowNode *fromId(WId id);
+ WId id() const;
+
+ OH_PixelmapNative *grab();
+
+ void setGeometry(const QRect &rect);
+ void move(int x, int y);
+ QRectF geometry() const;
+ void setVisible(bool visible);
+ bool isVisible() const;
+ void raise();
+ void lower();
+ QSize surfaceSize() const;
+ QColor backgroundColor() const;
+ void setBackgroundColor(const QColor &color);
+ void setWindowFlags(Qt::WindowFlags flags);
+ void createNode(const QRectF &rect, bool isFocusable);
+ void clear();
+ void setNodeFocus(bool isFocus);
+ bool isFocused() const;
+ QOhPlatformWindow *platformWindow() const;
+ bool isExternal() const;
+
+ int32_t zIndex() const;
+ QSharedPointer<QOhNativeNodeAPI> nodeApi() const;
+#if QT_CONFIG(vulkan)
+ VkSurfaceKHR *vkSurface() const;
+#endif
+ void setRenderFit(ArkUI_RenderFit fit);
+private:
+ void createOSRootNode(const QString &id, const QRectF &rect, bool scale = false);
+ void createOSChildNode(QOhWindowNode *parentNode, const QString &id, const QRectF &rect, bool scale = false);
+ void createOSNode(QOhWindowNode *parentNode, const QString &id, const QRectF &rect, bool scale);
+ void createNodeEventHandler();
+ void setFocusOnTouch(bool focusOnTouch);
+ void setSizeAdaptive();
+ void handleSubNodes(QOhWindowNode *parent);
+ static bool exist(ArkUI_NodeHandle stack, ArkUI_NodeHandle xcomponent);
+ static int32_t subNodeMaxZIndex(QOhWindowNode *node);
+ static int32_t subNodeMinZIndex(QOhWindowNode *node);
+private:
+ friend class QOhWindowNodeEventHandler;
+ QOhWindowNode *m_parent;
+ QOhWindowNodeEventHandler *m_eventHandler;
+ QOhPlatformWindow *m_window;
+ QOhNativeWindow *m_ownerWindow;
+ QOhXComponent *m_component;
+ OH_NativeXComponent *m_topComponent = nullptr;
+ void *m_nativeWindow = nullptr;
+ EGLSurface m_eglSurface = EGL_NO_SURFACE;
+ EGLDisplay m_display;
+ Node *m_id = nullptr;
+ QSharedPointer<QOhNativeNodeAPI> m_nodeApi;
+ ArkUI_NodeHandle m_stack = nullptr;
+ ArkUI_NodeHandle m_xcomponent = nullptr;
+ QList<QOhWindowNode *> m_children;
+ QList<QOhWindowNode *> m_deleteChildren;
+ int32_t m_zIndex = 0;
+ bool m_isExternal = false;
+ bool m_isIdCreateByQt = true;
+ std::promise<void> m_nativeWindowPromise;
+#if QT_CONFIG(vulkan)
+ mutable VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE;
+#endif
+};
+
+QT_END_NAMESPACE
+#endif // QOHWINDOWNODE_H
new file mode 100644
@@ -0,0 +1,1314 @@
+#include "qohwindownodeeventhandler.h"
+#include "qohplatformwindow.h"
+#include "qoheventdispatcher.h"
+#include "qohdisplaymanager.h"
+#include "qohnativewindow.h"
+#include "qohauxiliary.h"
+#include "qohdrag.h"
+#include "qohkeys.h"
+#include "qohevent.h"
+#include "qohwindowcontext.h"
+#include "qohnativenodeapi.h"
+#include "qohplatformopenglwindow.h"
+#include "qohplatformscreen.h"
+
+#include <QTimer>
+#include <QGuiApplication>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformcursor.h>
+#include <qpa/qplatformscreen.h>
+#include <qopenharmonydefines.h>
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qwindowsysteminterface_p.h>
+#include <private/qhighdpiscaling_p.h>
+
+#if OHOS_SDK_VERSION >= 17
+#include <arkui/native_key_event.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define RUN_QT_THREAD 0
+
+static bool isGestureSupported()
+{
+ auto gestureApi = reinterpret_cast<ArkUI_NativeGestureAPI_1 *>(
+ OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_GESTURE, "ArkUI_NativeGestureAPI_1"));
+ return (gestureApi->createGroupGesture != nullptr);
+}
+
+QOhWindowNodeEventHandler::QOhWindowNodeEventHandler(QOhWindowNode *node)
+ : m_node(node)
+ , m_inHanding(false)
+{
+ m_nodeApi = QOhNativeNodeAPI::getOrCreateForNode(node);
+ if (!node->isExternal()) {
+ if (isGestureSupported()) {
+ createGestureRecognizerForNode(node->m_xcomponent);
+ }
+#if QT_CONFIG(draganddrop)
+ m_dropTarget = new QOhDropTarget(node);
+#endif
+ }
+}
+
+QOhWindowNodeEventHandler::~QOhWindowNodeEventHandler()
+{
+ while (m_inHanding.load(std::memory_order_acquire))
+ std::this_thread::yield();
+#if QT_CONFIG(draganddrop)
+ if (m_dropTarget != nullptr) {
+ delete m_dropTarget;
+ m_dropTarget = nullptr;
+ }
+#endif
+ m_window.clear();
+}
+
+void QOhWindowNodeEventHandler::registerEvents(const std::vector<ArkUI_NodeEventType> &events)
+{
+ static int32_t id = 1;
+ bool result = false;
+ for (auto event : events) {
+ result = m_nodeApi->registerNodeEvent<QOhNativeNodeAPI::XCompoentType>(event, id, this);
+ if (!result) {
+ continue;
+ }
+ m_events.push_back(event);
+ id++;
+ }
+ m_nodeApi->addNodeEventReceiver<QOhNativeNodeAPI::XCompoentType>(nodeEvent);
+}
+
+void QOhWindowNodeEventHandler::unregisterEvents()
+{
+ for (auto event : m_events) {
+ m_nodeApi->unregisterNodeEvent<QOhNativeNodeAPI::XCompoentType>(event);
+ }
+ m_nodeApi->removeNodeEventReceiver<QOhNativeNodeAPI::XCompoentType>(nodeEvent);
+}
+
+void QOhWindowNodeEventHandler::setTargetWindow(QWindow *window)
+{
+ m_window = window;
+}
+
+extern QPoint translatePoint(const QPointF &global, QWindow *w);
+
+struct AtomicHelper
+{
+ AtomicHelper(std::atomic<bool> *i):m_i(i) { m_i->store(true, std::memory_order_release); }
+ ~AtomicHelper() { m_i->store(false, std::memory_order_release); }
+ std::atomic<bool> *m_i;
+};
+
+void QOhWindowNodeEventHandler::nodeEvent(ArkUI_NodeEvent *event)
+{
+ QOhWindowNodeEventHandler *handler = (QOhWindowNodeEventHandler *)OH_ArkUI_NodeEvent_GetUserData(event);
+ if (handler == nullptr || handler->m_window.isNull()) {
+ return;
+ }
+
+ AtomicHelper helper(&handler->m_inHanding);
+ Q_UNUSED(helper)
+
+ ArkUI_NodeEventType type = OH_ArkUI_NodeEvent_GetEventType(event);
+ switch(type) {
+#if OHOS_SDK_VERSION > 14
+ case NODE_TOUCH_EVENT:
+ {
+ ArkUI_UIInputEvent *inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
+ handler->touchEvent(inputEvent);
+ }
+ break;
+#endif
+ case NODE_ON_DROP:
+ case NODE_ON_DRAG_MOVE:
+ case NODE_ON_DRAG_ENTER:
+ case NODE_ON_DRAG_LEAVE:
+ handler->m_dropTarget->handleDragDropEvent(event);
+ break;
+ case NODE_ON_FOCUS:
+ handler->focusEvent(true);
+ break;
+ case NODE_ON_MOUSE:
+ {
+ ArkUI_UIInputEvent *inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
+ handler->mouseEvent(inputEvent);
+ }
+ break;
+ case NODE_ON_BLUR: {
+ handler->focusEvent(false);
+ }
+ break;
+ case NODE_ON_CLICK: {
+ }
+ break;
+ case NODE_ON_HOVER:
+ {
+ ArkUI_NodeComponentEvent *componentEvent = OH_ArkUI_NodeEvent_GetNodeComponentEvent(event);
+ if (!componentEvent) {
+ LOGE("NODE_ON_HOVER OH_ArkUI_NodeEvent_GetNodeComponentEvent faild.");
+ return;
+ }
+ handler->hoverEvent(1 == componentEvent->data[0].i32);
+ }
+ break;
+#if OHOS_SDK_VERSION >= 17
+ case NODE_ON_KEY_EVENT:
+ {
+ ArkUI_UIInputEvent *inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
+ handler->keyEvent(inputEvent);
+ }
+ break;
+ case NODE_ON_AXIS:
+ {
+ ArkUI_UIInputEvent *inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event);
+ handler->wheelEvent(inputEvent);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+void QOhWindowNodeEventHandler::hoverEvent(bool hover)
+{
+ if (m_window.isNull() || !QOhEventDispatcher::instance())
+ return;
+
+ QSharedPointer<QtOh::EnterLeaveEvent> e(new QtOh::EnterLeaveEvent());
+ e->enter = hover;
+ e->window = m_window;
+ QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance();
+ dispatcher->appendOhEvent(e);
+}
+
+#if OHOS_SDK_VERSION >= 17
+static inline Qt::KeyboardModifiers keyboardModifiersFromEvent(ArkUI_UIInputEvent *event)
+{
+ uint64_t keys = 0;
+ OH_ArkUI_UIInputEvent_GetModifierKeyStates(event, &keys);
+ return QOhKeys::ohKeys2Qt(keys) | QOhKeys::metaModifier();
+}
+#endif
+
+void QOhWindowNodeEventHandler::mouseEvent(ArkUI_UIInputEvent *event)
+{
+ if (!event) {
+ LOGE("NODE_ON_MOUSE OH_ArkUI_NodeEvent_GetInputEvent faild.");
+ return;
+ }
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(event);
+ // Todo 非客户区
+ auto displayX = OH_ArkUI_PointerEvent_GetDisplayX(event);
+ auto displayY = OH_ArkUI_PointerEvent_GetDisplayY(event);
+
+// // windowY包含了标题栏的高度,使用全局坐标转换
+// auto windowX = OH_ArkUI_PointerEvent_GetWindowX(event);
+// auto windowY = OH_ArkUI_PointerEvent_GetWindowY(event);
+ auto action = OH_ArkUI_MouseEvent_GetMouseAction(event);
+ auto button = OH_ArkUI_MouseEvent_GetMouseButton(event);
+#if OHOS_SDK_VERSION >= 17
+ auto keys = keyboardModifiersFromEvent(event);
+#endif
+
+#if 0
+ /* 移动事件处理:过滤事件 - 限制移动事件处理频率,6.0事件频率过高导致窗口移动卡顿 */
+ quint64 time = OH_ArkUI_UIInputEvent_GetEventTime(event);
+ if (action == UI_MOUSE_EVENT_ACTION_MOVE && QtOh::apiVersion() >= 20) {
+ static quint64 lastTimestamp = 0;
+ quint64 interval = time - lastTimestamp;
+ if (interval < MSTONS(15))
+ return;
+ lastTimestamp = time;
+ }
+#endif
+
+ if (!QOhEventDispatcher::instance())
+ return;
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (Q_UNLIKELY(!platformScreen)) {
+ qDebug() << "platformScreen is null. displayId: " << displayId;
+ return;
+ }
+ // arkui节点的默认单位为vp,因此需要做单位变换
+ auto scale = 1.0f;
+ auto screenTopLeft = QPoint();
+ if (platformScreen) {
+ scale = QtOh::densityPixels(platformScreen);
+ screenTopLeft = platformScreen->geometry().topLeft();
+ }
+
+ auto globalPos = QPointF { displayX, displayY };
+ globalPos = globalPos * scale;
+ globalPos += screenTopLeft;
+
+ if (QOhNativeWindow *nativeWindow = m_node->ownerWindow()) {
+ // 如果是在系统按钮的区域内,由系统响应不上传给Qt
+ if (nativeWindow->hitTest(globalPos.toPoint(), scale) == QOhNativeWindow::SystemButton)
+ return;
+ }
+
+ QSharedPointer<QtOh::MouseEvent> mouseEvent(new QtOh::MouseEvent());
+ switch (action) {
+ case UI_MOUSE_EVENT_ACTION_MOVE:
+ mouseEvent->mouseEventType = QEvent::MouseMove;
+ break;
+ case UI_MOUSE_EVENT_ACTION_PRESS:
+ mouseEvent->mouseEventType = QEvent::MouseButtonPress;
+ break;
+ case UI_MOUSE_EVENT_ACTION_RELEASE:
+ mouseEvent->mouseEventType = QEvent::MouseButtonRelease;
+ break;
+#if OHOS_SDK_VERSION >= 18
+ /* Qt鼠标没有cancel类型,视作release处理 */
+ case UI_MOUSE_EVENT_ACTION_CANCEL:
+ mouseEvent->mouseEventType = QEvent::MouseButtonRelease;
+ break;
+#endif
+ default:
+ return;
+ }
+
+ switch (button) {
+ case UI_MOUSE_EVENT_BUTTON_NONE:
+ mouseEvent->mouseButton = Qt::NoButton;
+ break;
+ case UI_MOUSE_EVENT_BUTTON_LEFT:
+ mouseEvent->mouseButton = Qt::LeftButton;
+ break;
+ case UI_MOUSE_EVENT_BUTTON_MIDDLE:
+ mouseEvent->mouseButton = Qt::MiddleButton;
+ break;
+ case UI_MOUSE_EVENT_BUTTON_RIGHT:
+ mouseEvent->mouseButton = Qt::RightButton;
+ break;
+ case UI_MOUSE_EVENT_BUTTON_FORWARD:
+ mouseEvent->mouseButton = Qt::ForwardButton;
+ break;
+ case UI_MOUSE_EVENT_BUTTON_BACK:
+ mouseEvent->mouseButton = Qt::BackButton;
+ break;
+ }
+#if OHOS_SDK_VERSION >= 17
+ mouseEvent->keyboardModifiers = keys;
+#endif
+ if (m_node->isExternal()) {
+ handleExternalNodeMouseEvent(globalPos, mouseEvent->mouseEventType,
+ mouseEvent->mouseButton, mouseEvent->keyboardModifiers);
+ return;
+ }
+ QWindow *window = m_window.data();
+ while (window && (window->flags() & Qt::WindowTransparentForInput))
+ window = window->parent();
+ if (!window || !window->handle())
+ return;
+
+ QPointF localPos = window->handle()->mapFromGlobal(globalPos.toPoint());
+ mouseEvent->window = window;
+ mouseEvent->xcPos = localPos;
+ mouseEvent->scPos = globalPos;
+
+ QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance();
+ dispatcher->appendOhEvent(mouseEvent);
+}
+
+void QOhWindowNodeEventHandler::handleExternalNodeMouseEvent(const QPointF &global, QEvent::Type type,
+ Qt::MouseButton button, Qt::KeyboardModifiers mod)
+{
+ if (QGuiApplicationPrivate::instance()->popupActive()) {
+ QWindow *grabber = QOhWindowContext::mouseGrabberWindow();
+ if (grabber == nullptr)
+ return;
+ QSharedPointer<QtOh::MouseEvent> synthesisEvent(new QtOh::MouseEvent());
+ synthesisEvent->mouseButton = button;
+ synthesisEvent->window = grabber;
+ synthesisEvent->xcPos = translatePoint(global, grabber);
+ synthesisEvent->scPos = global;
+ synthesisEvent->mouseEventType = type;
+ synthesisEvent->keyboardModifiers = mod;
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance())
+ dispatcher->appendOhEvent(synthesisEvent);
+ } else {
+ auto screens = QGuiApplication::screens();
+ for (int i = 0; i < screens.count(); ++i) {
+ if (QPlatformCursor *cursor = screens.at(i)->handle()->cursor())
+ cursor->setPos(global.toPoint());
+ }
+ }
+}
+
+#if OHOS_SDK_VERSION > 14
+
+/* 轻量级触摸点数据结构,用于跨线程传递
+ * 使用POD类型确保高效拷贝 */
+struct TouchPointData {
+ int32_t pointerId;
+ float displayX;
+ float displayY;
+ float pressure;
+
+ TouchPointData() = default;
+ TouchPointData(int32_t id, float x, float y, float p)
+ : pointerId(id), displayX(x), displayY(y), pressure(p) {}
+};
+
+void QOhWindowNodeEventHandler::translateMouseEvent(ArkUI_UIInputEvent *inputEvent)
+{
+ auto pw = m_node->platformWindow();
+ if (Q_UNLIKELY(!pw)) {
+ return;
+ }
+
+ /* 快速过滤鼠标事件
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0
+ * UI_INPUT_EVENT_SOURCE_TYPE_MOUSE = 1
+ * UI_INPUT_EVENT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+ if (sourceType == UI_INPUT_EVENT_SOURCE_TYPE_MOUSE) {
+ return;
+ }
+
+ const int32_t action = OH_ArkUI_UIInputEvent_GetAction(inputEvent);
+
+ /* 特殊事件处理:取消事件 - 立即处理并清除状态 */
+ if (Q_UNLIKELY(action == UI_TOUCH_EVENT_ACTION_CANCEL)) {
+ QWindow *window = pw->window();
+#if RUN_QT_THREAD
+ QtOh::runOnQtMainThread([this, window]{
+#endif
+ if (QOhWindowNode::nodeDestroyed(m_node))
+ return;
+ if (auto *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::TouchEvent> e(new QtOh::TouchEvent());
+ e->window = window;
+ e->actionType = UI_TOUCH_EVENT_ACTION_CANCEL;
+ dispatcher->appendOhEvent(e);
+ }
+ m_lastTouchPoints.clear();
+ m_pointerIdToTouchId.clear();
+
+#if RUN_QT_THREAD
+ });
+#endif
+
+ return;
+ }
+
+ const uint32_t pointCount = OH_ArkUI_PointerEvent_GetPointerCount(inputEvent);
+ if (Q_UNLIKELY(pointCount == 0)) {
+ return;
+ }
+
+ /* 在JS线程中只提取必要的原始数据 - 批量提取减少API调用 */
+ const int64_t time = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+ int32_t changedPointerId = 0;
+ if (Q_UNLIKELY(ARKUI_ERROR_CODE_NO_ERROR != OH_ArkUI_PointerEvent_GetChangedPointerId(inputEvent, (uint32_t*)&changedPointerId))) {
+ LOGE("get changedPointerId failed");
+ return;
+ }
+
+#if 0
+ /* 移动事件处理:过滤事件 - 限制移动事件处理频率,6.0事件频率过高导致窗口移动卡顿 */
+ if (action == UI_TOUCH_EVENT_ACTION_MOVE && QtOh::apiVersion() >= 20) {
+ static quint64 lastTimestamp = 0;
+ quint64 interval = time - lastTimestamp;
+ if (interval < MSTONS(15))
+ return;
+ lastTimestamp = time;
+ }
+#endif
+
+ const int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ const int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+#if OHOS_SDK_VERSION >= 17
+ const Qt::KeyboardModifiers keys = keyboardModifiersFromEvent(inputEvent);
+#endif
+ /* 直接使用QVector,通过reserve预分配内存,Lambda捕获时会进行值拷贝 */
+ QVector<TouchPointData> rawPoints;
+ rawPoints.reserve(pointCount);
+
+ for (uint32_t i = 0; i < pointCount; i++) {
+ rawPoints.append(TouchPointData(
+ OH_ArkUI_PointerEvent_GetPointerId(inputEvent, i),
+ OH_ArkUI_PointerEvent_GetDisplayXByIndex(inputEvent, i),
+ OH_ArkUI_PointerEvent_GetDisplayYByIndex(inputEvent, i),
+ OH_ArkUI_PointerEvent_GetPressure(inputEvent, i)
+ ));
+ }
+#if RUN_QT_THREAD
+ /* 将处理逻辑投递到Qt主线程
+ * rawPoints会被Lambda值捕获,确保数据在Qt线程中有效 */
+ QtOh::runOnQtMainThread([this, action, pointCount, changedPointerId, displayId, time, sourceType, toolType,
+ rawPoints = std::move(rawPoints)
+#if OHOS_SDK_VERSION >= 17
+ , keys
+#endif
+ ]() mutable {
+#endif
+ if (Q_UNLIKELY(QOhWindowNode::nodeDestroyed(m_node) || !QOhEventDispatcher::instance()))
+ return;
+
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (Q_UNLIKELY(!platformScreen)) {
+ return;
+ }
+
+ const qreal scale = QtOh::densityPixels(platformScreen);
+ const QRect screenGeometry = platformScreen->geometry();
+ const qreal screenWidth = screenGeometry.width();
+ const qreal screenHeight = screenGeometry.height();
+ const QPoint screenTopLeft = screenGeometry.topLeft();
+ const bool isDownAction = (action == UI_TOUCH_EVENT_ACTION_DOWN);
+ const bool isUpAction = (action == UI_TOUCH_EVENT_ACTION_UP);
+ const bool isMoveAction = (action == UI_TOUCH_EVENT_ACTION_MOVE);
+
+ /* 预分配内存,避免动态扩展 */
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+ touchPoints.reserve(pointCount + m_lastTouchPoints.size());
+
+ QSet<int> currentTouchIds;
+ currentTouchIds.reserve(pointCount);
+
+ /* 优化:合并阶段一和阶段二,减少循环次数 */
+ for (uint32_t i = 0; i < pointCount; i++) {
+ const TouchPointData &rawData = rawPoints[i];
+ const int32_t pointerId = rawData.pointerId;
+
+ /* 映射或创建touchId */
+ int touchId;
+ auto pointerIt = m_pointerIdToTouchId.find(pointerId);
+ if (pointerIt != m_pointerIdToTouchId.end()) {
+ touchId = pointerIt.value();
+ } else {
+ touchId = m_pointerIdToTouchId.size();
+ m_pointerIdToTouchId.insert(pointerId, touchId);
+ }
+ currentTouchIds.insert(touchId);
+
+ /* 计算位置 - 减少重复计算 */
+ const float displayX = rawData.displayX * scale;
+ const float displayY = rawData.displayY * scale;
+ const QPointF globalPointF(displayX, displayY);
+ const QPoint globalPos = globalPointF.toPoint() + screenTopLeft;
+
+ /* 构建触摸点 */
+ QWindowSystemInterface::TouchPoint touchPoint;
+ touchPoint.id = touchId;
+ touchPoint.pressure = rawData.pressure;
+ touchPoint.rotation = 0;
+ touchPoint.normalPosition = QPointF(globalPointF.x() / screenWidth,
+ globalPointF.y() / screenHeight);
+ touchPoint.area.setSize(QSize(10, 10));
+ touchPoint.area.moveCenter(globalPos);
+ touchPoint.rawPositions = QVector<QPointF> { globalPos, globalPointF };
+
+ const bool isChangedPointer = (pointerId == changedPointerId);
+ /* Qt 6 only modify */
+ /* 优化:使用if-else代替多个独立if,减少判断次数 */
+ if (isDownAction) {
+ if (isChangedPointer) {
+ touchPoint.state = QEventPoint::State::Pressed;
+ m_lastTouchPoints.insert(touchId, touchPoint);
+ } else {
+ touchPoint.state = m_lastTouchPoints.contains(touchId)
+ ? QEventPoint::State::Stationary
+ : QEventPoint::State::Pressed;
+ if (touchPoint.state == QEventPoint::State::Pressed) {
+ m_lastTouchPoints.insert(touchId, touchPoint);
+ }
+ }
+ } else if (isUpAction) {
+ if (isChangedPointer) {
+ touchPoint.state = QEventPoint::State::Released;
+ m_lastTouchPoints.remove(touchId);
+ m_pointerIdToTouchId.remove(pointerId);
+ } else {
+ touchPoint.state = QEventPoint::State::Stationary;
+ }
+ } else if (isMoveAction) {
+ /* 移动事件:参考Windows实现,直接比较归一化位置而非使用阈值 */
+ auto lastIt = m_lastTouchPoints.find(touchId);
+ if (lastIt != m_lastTouchPoints.end()) {
+ const QPointF& lastNormalPos = lastIt.value().normalPosition;
+ const bool stationaryTouchPoint = (touchPoint.normalPosition == lastNormalPos);
+
+ if (stationaryTouchPoint) {
+ /* 位置完全相同:复用上次的触摸点数据 */
+ touchPoint.state = QEventPoint::State::Stationary;
+ touchPoint.normalPosition = lastNormalPos;
+ touchPoint.rawPositions = lastIt.value().rawPositions;
+ touchPoint.area = lastIt.value().area;
+ } else {
+ /* 位置有变化:标记为移动并更新缓存 */
+ touchPoint.state = QEventPoint::State::Updated;
+ m_lastTouchPoints.insert(touchId, touchPoint);
+ }
+ } else {
+ /* 新的触摸点在MOVE事件中出现 */
+ touchPoint.state = QEventPoint::State::Updated;
+ m_lastTouchPoints.insert(touchId, touchPoint);
+ }
+ } else {
+ touchPoint.state = QEventPoint::State::Stationary;
+ }
+
+ touchPoints.append(touchPoint);
+ }
+
+ /* 优化:补充缺失触摸点 - 只在必要时才遍历 */
+ if (m_lastTouchPoints.size() > currentTouchIds.size()) {
+ for (auto it = m_lastTouchPoints.constBegin(); it != m_lastTouchPoints.constEnd(); ++it) {
+ if (!currentTouchIds.contains(it.key())) {
+ QWindowSystemInterface::TouchPoint stationaryPoint = it.value();
+ stationaryPoint.state = QEventPoint::State::Stationary;
+ touchPoints.append(stationaryPoint);
+ }
+ }
+ }
+
+ if (touchPoints.isEmpty()) {
+ return;
+ }
+
+ /* 优化:清理映射 - 检查是否所有点都释放 */
+ if (isUpAction && m_lastTouchPoints.isEmpty()) {
+ m_pointerIdToTouchId.clear();
+ }
+
+ /* 优化:对于MOVE事件,快速检查是否有真正的移动 */
+ bool shouldDispatch = true;
+ if (isMoveAction) {
+ bool hasRealMovement = false;
+ for (const auto& point : touchPoints) {
+ if (point.state == QEventPoint::State::Updated) {
+ hasRealMovement = true;
+ break;
+ }
+ }
+ shouldDispatch = hasRealMovement;
+ }
+
+ if (shouldDispatch) {
+ auto *dispatcher = QOhEventDispatcher::instance();
+ if (!dispatcher) {
+ return;
+ }
+
+ /* 更新触摸点位置 - 只更新必要的点 */
+ if (isDownAction || isMoveAction) {
+ for (const auto &point : touchPoints) {
+ if (point.state == QEventPoint::State::Updated || point.state == Qt::TouchPointPressed) {
+ dispatcher->updateLastTouchPoints(point.id, point.rawPositions.first());
+ }
+ }
+ }
+
+ QSharedPointer<QtOh::TouchEvent> e(new QtOh::TouchEvent());
+ e->id = changedPointerId;
+ e->touchPoints = touchPoints;
+ e->window = pw->window();
+ e->actionType = action;
+ e->timestamp = time;
+#if OHOS_SDK_VERSION >= 17
+ e->keyboardModifiers = keys;
+#endif
+ /* Qt 6 only modify */
+ e->deviceType = sourceType == UI_INPUT_EVENT_SOURCE_TYPE_TOUCH_SCREEN ?
+ QPointingDevice::DeviceType::TouchScreen : QPointingDevice::DeviceType::Mouse;
+ e->pointerType = QPointingDevice::PointerType::Unknown;
+ switch (toolType) {
+ case UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD:
+ case UI_INPUT_EVENT_TOOL_TYPE_FINGER:
+ e->pointerType = QPointingDevice::PointerType::Finger;
+ break;
+ case UI_INPUT_EVENT_TOOL_TYPE_PEN:
+ e->pointerType = QPointingDevice::PointerType::Pen;
+ break;
+ case UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK:
+ case UI_INPUT_EVENT_TOOL_TYPE_MOUSE:
+ e->pointerType = QPointingDevice::PointerType::Generic;
+ break;
+ default:
+ break;
+ }
+
+ dispatcher->appendOhEvent(e);
+ }
+#if RUN_QT_THREAD
+ });
+#endif
+}
+
+void QOhWindowNodeEventHandler::translatePenEvent(ArkUI_UIInputEvent *inputEvent)
+{
+ /* 在JS线程中提取必要的原始数据 */
+ const int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ const int32_t action = OH_ArkUI_UIInputEvent_GetAction(inputEvent);
+ const int32_t deviceId = OH_ArkUI_UIInputEvent_GetDeviceId(inputEvent);
+ const int64_t time = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ const float displayX = OH_ArkUI_PointerEvent_GetDisplayXByIndex(inputEvent, 0);
+ const float displayY = OH_ArkUI_PointerEvent_GetDisplayYByIndex(inputEvent, 0);
+ const float pressure = OH_ArkUI_PointerEvent_GetPressure(inputEvent, 0);
+ const float tiltX = OH_ArkUI_PointerEvent_GetTiltX(inputEvent, 0);
+ const float tiltY = OH_ArkUI_PointerEvent_GetTiltY(inputEvent, 0);
+
+#if OHOS_SDK_VERSION >= 17
+ double rotation = 0.f;
+ OH_ArkUI_PointerEvent_GetRollAngle(inputEvent, &rotation);
+#endif
+
+ QWindow *window = m_window;
+
+#if RUN_QT_THREAD
+ /* 将处理逻辑投递到Qt主线程 */
+ QtOh::runOnQtMainThread([this, window, displayId, action, deviceId, time,
+ displayX, displayY, pressure, tiltX, tiltY
+#if OHOS_SDK_VERSION >= 17
+ , rotation
+#endif
+ ]{
+#endif
+ if (Q_UNLIKELY(QOhWindowNode::nodeDestroyed(m_node) || !QOhEventDispatcher::instance()))
+ return;
+
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (Q_UNLIKELY(!platformScreen)) {
+ return;
+ }
+
+ const qreal scale = QtOh::densityPixels(platformScreen);
+ const QRect screenGeometry = platformScreen->geometry();
+
+ const float scaledX = displayX * scale;
+ const float scaledY = displayY * scale;
+ const QPointF globalPointF(scaledX, scaledY);
+ const QPoint globalPos = globalPointF.toPoint() + screenGeometry.topLeft();
+
+ QSharedPointer<QtOh::TabletEvent> e(new QtOh::TabletEvent());
+ e->timestamp = time;
+ e->presure = pressure;
+ e->global = globalPos;
+ e->uniqueID = deviceId;
+ e->btn = (action == UI_TOUCH_EVENT_ACTION_UP) ? Qt::NoButton : Qt::LeftButton;
+ e->window = window;
+#if OHOS_SDK_VERSION < 17
+ e->rotation = 0.f;
+#else
+ e->rotation = rotation;
+#endif
+
+ QOhEventDispatcher::instance()->appendOhEvent(e);
+#if RUN_QT_THREAD
+ });
+#endif
+}
+
+#endif
+
+#if OHOS_SDK_VERSION >= 17
+void QOhWindowNodeEventHandler::keyEvent(ArkUI_UIInputEvent *keyEvent)
+{
+ if (keyEvent == nullptr)
+ return;
+
+ auto type = OH_ArkUI_KeyEvent_GetType(keyEvent);
+ auto keyCode = OH_ArkUI_KeyEvent_GetKeyCode(keyEvent);
+ auto deviceId = OH_ArkUI_UIInputEvent_GetDeviceId(keyEvent);
+ auto text = QString::fromUtf8(OH_ArkUI_KeyEvent_GetKeyText(keyEvent));
+ auto keys = keyboardModifiersFromEvent(keyEvent);
+
+#if RUN_QT_THREAD
+ QtOh::runOnQtMainThread([this, type, keyCode, text, deviceId, keys] {
+#endif
+ if (QOhWindowNode::nodeDestroyed(m_node) || !QOhEventDispatcher::instance())
+ return;
+ QSharedPointer<QtOh::NewKeyEvent> e(new QtOh::NewKeyEvent());
+ e->window = m_window;
+ e->code = keyCode;
+ e->text = text;
+ e->type = type;
+ e->keyboardModifiers = keys;
+
+ QOhEventDispatcher::instance()->appendOhEvent(e);
+#if RUN_QT_THREAD
+ });
+#endif
+}
+
+void QOhWindowNodeEventHandler::wheelEvent(ArkUI_UIInputEvent *event)
+{
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetX(event);
+ float ly = OH_ArkUI_PointerEvent_GetY(event);
+
+ /* FIXME wanghao 多屏时获取当前输入回调的屏幕id错误 */
+#if 0
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(event);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(event);
+ auto displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(event);
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (platformScreen) {
+ gx += platformScreen->geometry().left();
+ gy += platformScreen->geometry().top();
+ }
+#endif
+
+ float scale = OH_ArkUI_AxisEvent_GetPinchAxisScaleValue(event);
+
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(event);
+
+#if OHOS_SDK_VERSION >= 15
+ int32_t axisAction = 0;
+ /* 获取当前轴事件的操作类型的值
+ * UI_AXIS_EVENT_ACTION_NONE = 0, // The axis event is abnormal.
+ * UI_AXIS_EVENT_ACTION_BEGIN = 1, // The axis event begins.
+ * UI_AXIS_EVENT_ACTION_UPDATE = 2, // The axis event is updated.
+ * UI_AXIS_EVENT_ACTION_END = 3, // The axis event ends.
+ * UI_AXIS_EVENT_ACTION_CANCEL = 4, // The axis event is canceled.
+ */
+ axisAction = OH_ArkUI_AxisEvent_GetAxisAction(event);
+
+ /* NOTE 鼠标滚轮只处理UPDATE? */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType &&
+ UI_AXIS_EVENT_ACTION_UPDATE != axisAction) {
+ return;
+ }
+#endif
+
+#if OHOS_SDK_VERSION >= 17
+ int32_t scrollStep = OH_ArkUI_AxisEvent_GetScrollStep(event);
+#endif
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(event);
+ double verticalValue = OH_ArkUI_AxisEvent_GetVerticalAxisValue(event);
+ double horizontalValue = OH_ArkUI_AxisEvent_GetHorizontalAxisValue(event);
+ auto keys = keyboardModifiersFromEvent(event);
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::WheelEvent> e(new QtOh::WheelEvent());
+ e->pinchScale = scale;
+ e->toolType = toolType;
+ e->timestamp = timestamp;
+
+#if OHOS_SDK_VERSION >= 15
+ e->axisAction = axisAction;
+#endif
+
+#if OHOS_SDK_VERSION >= 17
+ e->scrollStep = scrollStep;
+#endif
+ e->sourceType = sourceType;
+ qreal dpr = QtOh::densityPixels(m_window->handle()->screen());
+ e->local = QPointF(lx * dpr, ly * dpr);
+ e->window = m_window;
+ e->global = e->window->handle()->mapToGlobal(e->local.toPoint());
+
+ e->verticalValue = verticalValue;
+ e->horizontalValue = horizontalValue;
+ e->keyboardModifiers = keys;
+ dispatcher->appendOhEvent(e);
+ }
+}
+#endif
+
+void QOhWindowNodeEventHandler::focusEvent(bool focusIn)
+{
+ // 焦点切换有时不触发, 顶层节点的焦点由窗口windowEvent处理
+ if (m_window.isNull() || m_window->parent() == nullptr)
+ return;
+ QWindow *nextActiveWindow = 0;
+ if (focusIn) {
+ auto w = m_window;
+ QWindow *topWindow = QOhWindowContext::topLevelOf(w);
+ QWindow *modalWindow = 0;
+ if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) {
+ modalWindow->requestActivate();
+ return;
+ }
+ nextActiveWindow = w;
+ } else {
+ /* 是否有系统接口获取失去焦点时怎么查找系统的焦点node,
+ * 获取焦点的node要比失去焦点的node后发送foucsIn事件
+ */
+ QOhWindowNode *focusNode = QOhWindowNode::focusNode();
+ if (focusNode != nullptr) {
+ auto nextActivePlatformWindow = focusNode->m_window;
+ if (nextActivePlatformWindow)
+ nextActiveWindow = nextActivePlatformWindow->window();
+ }
+ }
+ QOhWindowContext::setFocusWindow(nextActiveWindow);
+}
+
+#if OHOS_SDK_VERSION > 14
+void QOhWindowNodeEventHandler::touchEvent(ArkUI_UIInputEvent *inputEvent)
+{
+ /* 产生输入事件的工具类型定义
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+ if (UI_INPUT_EVENT_TOOL_TYPE_PEN == sourceType &&
+ UI_INPUT_EVENT_TOOL_TYPE_FINGER != toolType) {
+ translatePenEvent(inputEvent);
+ } else {
+ translateMouseEvent(inputEvent);
+ }
+}
+#endif
+
+bool QOhWindowNodeEventHandler::isValidGestureEvent(ArkUI_GestureEvent *event, QOhWindowNodeEventHandler *handler)
+{
+ if ((nullptr == event) || (nullptr == handler) || handler->m_window.isNull())
+ return false;
+ return true;
+}
+
+void QOhWindowNodeEventHandler::createGestureRecognizerForNode(ArkUI_NodeHandle node)
+{
+ auto gestureApi = reinterpret_cast<ArkUI_NativeGestureAPI_1 *>(
+ OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_GESTURE, "ArkUI_NativeGestureAPI_1"));
+ /* 创建手势组 */
+ auto groupGesture = gestureApi->createGroupGesture(ArkUI_GroupGestureMode::PARALLEL_GROUP);
+
+ /* 创建拖动手势 */
+ auto panGesture = gestureApi->createPanGesture(1, GESTURE_DIRECTION_ALL, 5);
+ int32_t result = gestureApi->setGestureEventTarget(panGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhWindowNodeEventHandler::handlePanGestureEvent);
+
+ if (0 != result)
+ LOGE("set panGesture Target Failed.");
+
+ /* 创建捏合手势 */
+ auto pinchGesture = gestureApi->createPinchGesture(2, 0);
+ result = gestureApi->setGestureEventTarget(pinchGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhWindowNodeEventHandler::handlePinchGestureEvent);
+ if (0 != result)
+ LOGE("set pinchGesture Target Failed.");
+
+ /* 创建滑动手势 */
+ auto swipeGesture = gestureApi->createSwipeGesture(1, GESTURE_DIRECTION_ALL, 0);
+ result = gestureApi->setGestureEventTarget(swipeGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhWindowNodeEventHandler::handleSwipeGestureEvent);
+ if (0 != result)
+ LOGE("set swipeGesture Target Failed.");
+
+ /* 创建旋转手势 */
+ auto rotationGesture = gestureApi->createRotationGesture(2, 0);
+ result = gestureApi->setGestureEventTarget(rotationGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhWindowNodeEventHandler::handleRotationGestureEvent);
+
+ /* 创建长按手势 */
+ auto longPressGesture = gestureApi->createLongPressGesture(1, false, 500);
+ gestureApi->setGestureEventTarget(longPressGesture,
+ GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE |
+ GESTURE_EVENT_ACTION_END | GESTURE_EVENT_ACTION_CANCEL,
+ this, QOhWindowNodeEventHandler::handleLongPressGesture);
+
+ /* 将手势添加到手势组 */
+ if (gestureApi->addChildGesture) {
+ /* FIXME panGesture造成无边框时三键位置无响应 */
+ //gestureApi->addChildGesture(groupGesture, panGesture);
+ gestureApi->addChildGesture(groupGesture, pinchGesture);
+ gestureApi->addChildGesture(groupGesture, longPressGesture);
+ //gestureApi->addChildGesture(groupGesture, swipeGesture);
+ //gestureApi->addChildGesture(groupGesture, rotationGesture);
+ }
+
+ /* 将手势组设置到组件上 */
+ gestureApi->addGestureToNode(node, groupGesture, PARALLEL, NORMAL_GESTURE_MASK);
+}
+
+static QPointF fix_global_point(const QPointF &pt, int32_t displayId)
+{
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ QPointF result = pt;
+ if (platformScreen) {
+ result += platformScreen->geometry().topLeft();
+ }
+ return result;
+}
+
+void QOhWindowNodeEventHandler::handlePanGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhWindowNodeEventHandler *handler = reinterpret_cast<QOhWindowNodeEventHandler*>(extraParam);
+ if (!isValidGestureEvent(event, handler))
+ return;
+ handler->panGesture(event);
+}
+
+void QOhWindowNodeEventHandler::panGesture(ArkUI_GestureEvent *event)
+{
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ float velocity = OH_ArkUI_PanGesture_GetVelocity(event);
+
+ /* GESTURE_EVENT_ACTION_END = 0x04
+ * GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::PanNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = velocity;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx,gy), displayId);
+ e->sourceType = sourceType;
+ e->gestureType = gestureType(actionType);
+ e->window = m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhWindowNodeEventHandler::handleLongPressGesture(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhWindowNodeEventHandler *handler = reinterpret_cast<QOhWindowNodeEventHandler*>(extraParam);
+ if (!isValidGestureEvent(event, handler))
+ return;
+
+ handler->longPressGesture(event);
+}
+
+void QOhWindowNodeEventHandler::longPressGesture(ArkUI_GestureEvent *event)
+{
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ float scale = OH_ArkUI_PinchGesture_GetScale(event);
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+ bool mouseTriggered = (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType || UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType);
+ /* NOTE 将长按手势绑定到Qt的QContextMenu事件
+ * 只处理开始的通知,避免重复发送事件通知
+ */
+ if (GESTURE_EVENT_ACTION_ACCEPT == actionType) {
+ // Todo 从event 获取mods
+ QWindowSystemInterface::handleContextMenuEvent(m_window, mouseTriggered, QPointF(lx, ly).toPoint(), fix_global_point(QPointF(gx, gy), displayId).toPoint(),
+ QOhKeys::keyboardModifiers());
+ }
+}
+
+void QOhWindowNodeEventHandler::handlePinchGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhWindowNodeEventHandler *handler = reinterpret_cast<QOhWindowNodeEventHandler*>(extraParam);
+ if (!isValidGestureEvent(event, handler))
+ return;
+
+ handler->pinchGestureEvent(event);
+}
+
+void QOhWindowNodeEventHandler::pinchGestureEvent(ArkUI_GestureEvent *event)
+{
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ /* 过滤来自鼠标/触控板的系统手势 */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType ||
+ UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType) {
+ return;
+ }
+
+ float scale = OH_ArkUI_PinchGesture_GetScale(event);
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::ZoomNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = scale;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->sourceType = sourceType;
+ e->gestureType = gestureType(actionType);
+ e->window = m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhWindowNodeEventHandler::handleSwipeGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhWindowNodeEventHandler *handler = reinterpret_cast<QOhWindowNodeEventHandler*>(extraParam);
+ if (!isValidGestureEvent(event, handler))
+ return;
+
+ handler->swipeGestureEvent(event);
+}
+
+void QOhWindowNodeEventHandler::swipeGestureEvent(ArkUI_GestureEvent *event)
+{
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+ float angle = OH_ArkUI_SwipeGesture_GetAngle(event);
+ float velocity = OH_ArkUI_SwipeGesture_GetVelocity(event);
+
+ /* GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02,
+ * GESTURE_EVENT_ACTION_END = 0x04,
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::SwipeNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = velocity;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->sourceType = sourceType;
+ e->gestureType = gestureType(actionType);
+ e->window = m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+void QOhWindowNodeEventHandler::handleRotationGestureEvent(ArkUI_GestureEvent *event, void *extraParam)
+{
+ QOhWindowNodeEventHandler *handler = reinterpret_cast<QOhWindowNodeEventHandler*>(extraParam);
+ if (!isValidGestureEvent(event, handler))
+ return;
+ handler->rotationGestureEvent(event);
+}
+
+void QOhWindowNodeEventHandler::rotationGestureEvent(ArkUI_GestureEvent *event)
+{
+ auto inputEvent = OH_ArkUI_GestureEvent_GetRawInputEvent(event);
+#if 1
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(inputEvent);
+
+ /* 过滤来自鼠标/触控板的系统手势 */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType ||
+ UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD == toolType) {
+ return;
+ }
+#endif
+ /* GESTURE_EVENT_ACTION_END = 0x04
+ * GESTURE_EVENT_ACTION_ACCEPT = 0x01
+ * GESTURE_EVENT_ACTION_UPDATE = 0x02
+ * GESTURE_EVENT_ACTION_CANCEL = 0x08
+ */
+ ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);
+
+ float angle = OH_ArkUI_RotationGesture_GetAngle(event);
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(inputEvent);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(inputEvent);
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(inputEvent);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(inputEvent);
+ int32_t displayId = OH_ArkUI_UIInputEvent_GetTargetDisplayId(inputEvent);
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetWindowX(inputEvent);
+ float ly = OH_ArkUI_PointerEvent_GetWindowY(inputEvent);
+
+ auto gestureType = [](int actionType) {
+ switch (actionType) {
+ case GESTURE_EVENT_ACTION_END:
+ return Qt::EndNativeGesture;
+ case GESTURE_EVENT_ACTION_ACCEPT:
+ return Qt::BeginNativeGesture;
+ default:
+ break;
+ }
+ return Qt::RotateNativeGesture;
+ };
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::GestureEvent> e(new QtOh::GestureEvent());
+ e->value = angle;
+ e->timestamp = timestamp;
+ e->local = QPointF(lx, ly);
+ e->global = fix_global_point(QPointF(gx, gy), displayId);
+ e->sourceType = sourceType;
+ e->gestureType = gestureType(actionType);
+ e->window = m_window;
+ dispatcher->appendOhEvent(e);
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,75 @@
+#ifndef QOHWINDOWNODEEVENTHANDLER_H
+#define QOHWINDOWNODEEVENTHANDLER_H
+
+#include <arkui/native_node.h>
+#include <arkui/native_interface.h>
+#include <arkui/native_gesture.h>
+#include <QtCore/QList>
+#include <QtCore/QEvent>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QHash>
+#include <arkui/native_gesture.h>
+#include "qohwindownode.h"
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(draganddrop)
+class QOhDropTarget;
+#endif
+class QOhNativeNodeAPI;
+class QOhWindowNodeEventHandler
+{
+public:
+ QOhWindowNodeEventHandler(QOhWindowNode *node);
+ ~QOhWindowNodeEventHandler();
+ void registerEvents(const std::vector<ArkUI_NodeEventType> &events);
+ void unregisterEvents();
+ void setTargetWindow(QWindow *window);
+private:
+ static void nodeEvent(ArkUI_NodeEvent *event);
+ void focusEvent(bool focusIn);
+ void hoverEvent(bool hover);
+#if OHOS_SDK_VERSION > 14
+ void touchEvent(ArkUI_UIInputEvent *inputEvent);
+#endif
+ void mouseEvent(ArkUI_UIInputEvent *event);
+#if OHOS_SDK_VERSION >= 17
+ void keyEvent(ArkUI_UIInputEvent *keyEvent);
+ void wheelEvent(ArkUI_UIInputEvent *event);
+#endif
+private:
+ void handleExternalNodeMouseEvent(const QPointF &global, QEvent::Type type,
+ Qt::MouseButton button, Qt::KeyboardModifiers mod);
+#if OHOS_SDK_VERSION > 14
+ void translateMouseEvent(ArkUI_UIInputEvent *inputEvent);
+ void translatePenEvent(ArkUI_UIInputEvent *inputEvent);
+ QHash<int32_t, int> m_pointerIdToTouchId;
+ QHash<int, QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
+#endif
+ static bool isValidGestureEvent(ArkUI_GestureEvent *event, QOhWindowNodeEventHandler *handler);
+ void createGestureRecognizerForNode(ArkUI_NodeHandle node);
+ static void handlePanGestureEvent(ArkUI_GestureEvent *event, void *extraParam);
+ void panGesture(ArkUI_GestureEvent *event);
+ static void handleLongPressGesture(ArkUI_GestureEvent *event, void *extraParam);
+ void longPressGesture(ArkUI_GestureEvent *event);
+ static void handlePinchGestureEvent(ArkUI_GestureEvent *event, void *extraParam);
+ void pinchGestureEvent(ArkUI_GestureEvent *event);
+ static void handleSwipeGestureEvent(ArkUI_GestureEvent *event, void *extraParam);
+ void swipeGestureEvent(ArkUI_GestureEvent *event);
+ static void handleRotationGestureEvent(ArkUI_GestureEvent *event, void *extraParam);
+ void rotationGestureEvent(ArkUI_GestureEvent *event);
+private:
+ QOhWindowNode *m_node;
+ std::vector<ArkUI_NodeEventType> m_events;
+#if QT_CONFIG(draganddrop)
+ QOhDropTarget *m_dropTarget = nullptr;
+#endif
+ QPointer<QWindow> m_window;
+ std::atomic<bool> m_inHanding;
+ QSharedPointer<QOhNativeNodeAPI> m_nodeApi;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOHWINDOWNODEEVENTHANDLER_H
new file mode 100644
@@ -0,0 +1,556 @@
+#include <QtMath>
+#include <QThread>
+#include <qopenharmonydefines.h>
+
+#include "qohkeys.h"
+#include "qohmain.h"
+#include "qohevent.h"
+#include "qohauxiliary.h"
+#include "qohxcomponent.h"
+#include "qohwindownode.h"
+#include "qohwindowcontext.h"
+#include "qohplatformscreen.h"
+#include "qohplatformwindow.h"
+#include "qohdisplaymanager.h"
+#include "qoheventdispatcher.h"
+#include "qohplatforminputcontext.h"
+
+OH_NativeXComponent_Callback QOhXComponent::m_componentCallback = []() {
+ OH_NativeXComponent_Callback cb{};
+ cb.OnSurfaceCreated = &QOhXComponent::onSurfaceCreated;
+ cb.OnSurfaceChanged = &QOhXComponent::onSurfaceChanged;
+ cb.OnSurfaceDestroyed = &QOhXComponent::onSurfaceDestroyed;
+ if (OHOS_SDK_VERSION < 15 || QtOh::isOpenHarmonyDevice()) {
+ cb.DispatchTouchEvent = &QOhXComponent::dispatchTouchEvent;
+ } else {
+ cb.DispatchTouchEvent = nullptr;
+ }
+ return cb;
+}();
+
+OH_NativeXComponent_MouseEvent_Callback QOhXComponent::m_mouseEventCallback = {
+ &QOhXComponent::dispatchMouseEvent,
+ nullptr
+};
+
+QMutex QOhXComponent::m_compoentMutex;
+
+QOhXComponent::QOhXComponent(QOhWindowNode *node, OH_NativeXComponent *component)
+ : m_nativeComponent(component)
+ , m_node(node)
+{
+ {
+ QMutexLocker locker(&m_compoentMutex);
+ Q_UNUSED(locker)
+ m_allXCompoents << this;
+ }
+ initXComponent(component);
+}
+
+QOhXComponent::~QOhXComponent()
+{
+ QMutexLocker locker(&m_compoentMutex);
+ Q_UNUSED(locker)
+ m_allXCompoents.removeOne(this);
+}
+
+QString QOhXComponent::name() const
+{
+ return getName(m_nativeComponent);
+}
+
+QOhXComponent *QOhXComponent::find(OH_NativeXComponent *component)
+{
+ QMutexLocker locker(&m_compoentMutex);
+ auto it = std::find_if(m_allXCompoents.constBegin(), m_allXCompoents.constEnd(), [component](QOhXComponent *xc){
+ return (xc->nativeComponent() == component);
+ });
+ if (it == m_allXCompoents.constEnd())
+ return nullptr;
+ return *it;
+}
+
+void QOhXComponent::onSurfaceCreated(OH_NativeXComponent *component, void *window)
+{
+ LOGI("%{public}s %{public}s", Q_FUNC_INFO, qPrintable(getName(component)));
+ auto xc = find(component);
+ if (xc != nullptr)
+ xc->setNativeWindow(window);
+}
+
+void QOhXComponent::onSurfaceChanged(OH_NativeXComponent *component, void *window)
+{
+ uint64_t width;
+ uint64_t height;
+
+ int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("Get surface size failed");
+ }
+
+ QtOh::runOnQtMainThread([component, width, height] {
+ auto xc = find(component);
+ if (xc == nullptr)
+ return;
+ xc->handleSurfaceChanged(QSize(width, height));
+ });
+}
+
+void QOhXComponent::onSurfaceDestroyed(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(component);
+ Q_UNUSED(window);
+ QtOh::runOnQtMainThread([component]{
+ auto xc = find(component);
+ if (xc != nullptr)
+ xc->setNativeWindow(nullptr);
+ });
+}
+
+void QOhXComponent::handleWindowStatusEvent(QtOh::WindowStatusType event)
+{
+ if (m_node != nullptr && m_node->platformWindow() != nullptr){
+ QtOh::runOnQtMainThread([event, this]{
+ if (QOhWindowNode::nodeDestroyed(m_node))
+ return ;
+ if (QOhPlatformWindow *window = m_node->platformWindow()) {
+ window->handleWindowStatusEvent(event);
+ }
+ });
+ }
+}
+
+void QOhXComponent::onSurfaceHided(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(window);
+ LOGW("onSurfaceHided: %{public}s", qPrintable(getName(component)));
+ QOhXComponent *xc = find(component);
+ if (xc == nullptr)
+ return;
+
+ xc->handleWindowStatusEvent(QtOh::WindowStatusType::MINIMIZE);
+}
+
+void QOhXComponent::onSurfaceShow(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(window);
+ LOGW("onSurfaceShow: %{public}s", qPrintable(getName(component)));
+ QOhXComponent *xc = find(component);
+ if (xc == nullptr)
+ return;
+ xc->handleWindowStatusEvent(QtOh::WindowStatusType::SURFACE_SHOW);
+}
+
+bool QOhXComponent::dispatchKeyEvent(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(window);
+ QOhPlatformInputContext *context = QOhPlatformInputContext::ohInputContext();
+ if (Q_NULLPTR == component || Q_NULLPTR == context)
+ return true;
+
+ OH_NativeXComponent_KeyEvent *keyEvent = Q_NULLPTR;
+ /* 获取由XComponent触发的按键事件 */
+ if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
+ OH_NativeXComponent_KeyAction action;
+ OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); /* 获取按键事件的动作 */
+
+ OH_NativeXComponent_KeyCode code;
+ OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); /* 获取按键事件的键码值 */
+
+ OH_NativeXComponent_EventSourceType sourceType;
+ OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType); /* 获取按键事件的输入源类型 */
+
+ int64_t deviceId;
+ OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId); /* 获取按键事件的设备ID */
+
+ int64_t timeStamp;
+ OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); /* 获取按键事件的时间戳 */
+
+ QtOh::runOnQtMainThread([qWindow = QPointer<QWindow>(windowFromXComponent(component)), code, action, sourceType, deviceId, timeStamp]{
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::KeyEvent> e(new QtOh::KeyEvent());
+ e->window = qWindow;
+ e->code = code;
+ e->action = action;
+ e->stype = sourceType;
+ e->deviceId = deviceId;
+ e->timeStamp = timeStamp;
+ e->keyboardModifiers = QOhKeys::keyboardModifiers();
+ dispatcher->appendOhEvent(e);
+ }
+ });
+ } else {
+ LOGW("Get Key Event Error");
+ }
+
+ return true;
+}
+
+void QOhXComponent::dispatchTouchEvent(OH_NativeXComponent *component, void *window)
+{
+ if (nullptr == component || nullptr == window)
+ return;
+
+ /* NOTE 触摸板会触发touch事件和mouse事件,导致2次鼠标事件 */
+ OH_NativeXComponent_TouchEvent touchEvent;
+ int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
+ if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != ret) {
+ LOGE("get touch event failed, error code: [%{public}d]", ret);
+ return;
+ }
+
+ OH_NativeXComponent_EventSourceType sourceType(OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN);
+ ret = OH_NativeXComponent_GetTouchEventSourceType(component, touchEvent.id, &sourceType);
+ if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != ret) {
+ LOGE("get touch source type failed, code: [%{public}d]", ret);
+ return;
+ }
+
+ /* FIXME 不处理鼠标触发的touch */
+ if (OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE == sourceType ||
+ OH_NATIVEXCOMPONENT_SOURCE_TYPE_UNKNOWN == sourceType) {
+ //LOGW("input source type not process, type: [%{public}d]", sourceType);
+ return;
+ }
+
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+ auto qw = windowFromXComponent(component);
+ QPlatformScreen *screen = qw->screen()->handle();
+ QRect rc = screen->geometry();
+ for (int i = 0; i < int(touchEvent.numPoints); i++) {
+ OH_NativeXComponent_TouchPoint &point = touchEvent.touchPoints[i];
+ QWindowSystemInterface::TouchPoint touchPoint;
+ touchPoint.id = point.id;
+ touchPoint.pressure = point.force;
+ touchPoint.rotation = 0;
+ touchPoint.area.setSize(QSize(100, 100));
+ float globalX, globalY;
+ OH_NativeXComponent_GetTouchPointDisplayX(component, point.id, &globalX);
+ OH_NativeXComponent_GetTouchPointDisplayY(component, point.id, &globalY);
+ touchPoint.area.moveCenter(QPointF(globalX, globalY));
+ touchPoint.rawPositions = QVector<QPointF> { QPointF(globalX, globalY).toPoint()
+ , QPointF(globalX, globalY) };
+ /* Qt 6 only modify */
+ touchPoint.normalPosition = QPointF(globalX / rc.width(), globalY / rc.height());
+ touchPoint.state = QEventPoint::State::Updated;
+ if (OH_NATIVEXCOMPONENT_DOWN == point.type)
+ touchPoint.state = QEventPoint::State::Pressed;
+
+ if (OH_NATIVEXCOMPONENT_UP == point.type ||
+ OH_NATIVEXCOMPONENT_CANCEL == point.type)
+ touchPoint.state = QEventPoint::State::Released;
+
+ if (OH_NATIVEXCOMPONENT_MOVE == point.type) {
+ touchPoint.state = QEventPoint::State::Updated;
+ }
+
+ touchPoints.push_back(touchPoint);
+ }
+ /* Qt 6 only modify */
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::TouchEvent> e(new QtOh::TouchEvent());
+ e->id = touchEvent.id;
+ e->pointerType = QPointingDevice::PointerType::Unknown;
+ e->deviceType = sourceType == OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE ? QPointingDevice::DeviceType::Mouse :
+ QPointingDevice::DeviceType::TouchScreen;
+ switch (sourceType) {
+ case OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHSCREEN:
+ case OH_NATIVEXCOMPONENT_SOURCE_TYPE_TOUCHPAD:
+ e->pointerType = QPointingDevice::PointerType::Finger;
+ break;
+ case OH_NATIVEXCOMPONENT_SOURCE_TYPE_JOYSTICK:
+ case OH_NATIVEXCOMPONENT_SOURCE_TYPE_MOUSE:
+ case OH_NATIVEXCOMPONENT_SOURCE_TYPE_KEYBOARD:
+ e->pointerType = QPointingDevice::PointerType::Generic;
+ break;
+ default:
+ break;
+ }
+ e->touchPoints = touchPoints;
+ e->window = qw;
+ e->actionType = touchEvent.type;
+ e->timestamp = touchEvent.timeStamp;
+ dispatcher->appendOhEvent(e);
+ }
+
+}
+
+void QOhXComponent::dispatchMouseEvent(OH_NativeXComponent *component, void *window)
+{
+ if (QOhPlatformInputContext::ohInputContext() == nullptr)
+ return;
+
+ OH_NativeXComponent_MouseEvent mouseEvent;
+ int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS)
+ return;
+
+ OH_NativeXComponent_MouseEventAction action = mouseEvent.action;
+ OH_NativeXComponent_MouseEventButton button = mouseEvent.button;
+ Qt::MouseButton qbtn = Qt::NoButton;
+ switch (button) {
+ case OH_NATIVEXCOMPONENT_LEFT_BUTTON:
+ qbtn = Qt::LeftButton;
+ break;
+ case OH_NATIVEXCOMPONENT_RIGHT_BUTTON:
+ qbtn = Qt::RightButton;
+ break;
+ case OH_NATIVEXCOMPONENT_MIDDLE_BUTTON:
+ qbtn = Qt::MiddleButton;
+ break;
+ case OH_NATIVEXCOMPONENT_BACK_BUTTON:
+ qbtn = Qt::BackButton;
+ break;
+ case OH_NATIVEXCOMPONENT_FORWARD_BUTTON:
+ qbtn = Qt::ForwardButton;
+ break;
+ default:
+ break;
+ }
+
+ // LOGW("xid %{public}s, mouse event %{public}d, button is %{public}d screen point (%{public}f, %{public}f) xc point (%{public}f, %{public}f)", qPrintable(getName(component)), action, qbtn,
+ // mouseEvent.screenX, mouseEvent.screenY,mouseEvent.x, mouseEvent.y);
+
+ QSharedPointer<QtOh::MouseEvent> event(new QtOh::MouseEvent());
+ event->mouseButton = qbtn;
+ event->window = windowFromXComponent(component);
+ event->xcPos = QPointF(mouseEvent.x, mouseEvent.y);
+ event->scPos = QPointF(mouseEvent.screenX, mouseEvent.screenY);
+
+ switch (action) {
+ case OH_NATIVEXCOMPONENT_MOUSE_RELEASE:
+ event->mouseEventType = QEvent::MouseButtonRelease;
+ break;
+ case OH_NATIVEXCOMPONENT_MOUSE_PRESS:
+ event->mouseEventType = QEvent::MouseButtonPress;
+ break;
+ case OH_NATIVEXCOMPONENT_MOUSE_MOVE:
+ event->mouseEventType = QEvent::MouseMove;
+ break;
+ default:
+ event->mouseEventType = QEvent::None;
+ break;
+ }
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance())
+ dispatcher->appendOhEvent(event);
+}
+
+void QOhXComponent::dispatchHoverEvent(OH_NativeXComponent *component, bool isHover)
+{
+ /* NOTE 已经使用NODE_ON_HOVER注册hover进入离开事件 */
+ Q_UNUSED(component);
+ Q_UNUSED(isHover);
+}
+
+QString QOhXComponent::getName(OH_NativeXComponent *component)
+{
+ char id[OH_XCOMPONENT_ID_LEN_MAX + 1] = { };
+ uint64_t id_length = OH_XCOMPONENT_ID_LEN_MAX + 1;
+
+ int32_t ret = OH_NativeXComponent_GetXComponentId(component, id, &id_length);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ return QString();
+ }
+
+ return QString::fromLatin1(id);
+}
+
+QWindow *QOhXComponent::windowFromXComponent(OH_NativeXComponent *component)
+{
+ if (component == nullptr)
+ return nullptr;
+ QPlatformWindow *window = QOhWindowContext::get(QOhXComponent::getName(component));
+ return window == nullptr ? nullptr : window->window();
+}
+
+#if QT_CONFIG(wheelevent)
+void QOhXComponent::dispatchArkUIInputEvent(OH_NativeXComponent *component, ArkUI_UIInputEvent *event,
+ ArkUI_UIInputEvent_Type type)
+{
+ Q_UNUSED(type);
+ if (nullptr == event)
+ return;
+
+
+ /* 从带有指向性的输入事件中获取相对于当前屏幕左上角的坐标 */
+ float gx = OH_ArkUI_PointerEvent_GetDisplayX(event);
+ float gy = OH_ArkUI_PointerEvent_GetDisplayY(event);
+
+ /* 获取特定接触点相对于当前应用窗口左上角的坐标 */
+ float lx = OH_ArkUI_PointerEvent_GetX(event);
+ float ly = OH_ArkUI_PointerEvent_GetY(event);
+
+/* FIXME wanghao 多屏时获取当前输入回调的屏幕id错误 */
+#if 0
+ auto displayId= OH_ArkUI_UIInputEvent_GetTargetDisplayId(event);
+ auto platformScreen = QOhDisplayManager::instance()->platformScreenOrNull(displayId);
+ if (platformScreen) {
+ gx += platformScreen->geometry().left();
+ gy += platformScreen->geometry().top();
+ }
+#endif
+
+ float scale = OH_ArkUI_AxisEvent_GetPinchAxisScaleValue(event);
+
+ /* 获取产生UI输入事件的工具类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_TOOL_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENT_TOOL_TYPE_FINGER = 1,
+ * UI_INPUT_EVENT_TOOL_TYPE_PEN = 2,
+ * UI_INPUT_EVENT_TOOL_TYPE_MOUSE = 3,
+ * UI_INPUT_EVENT_TOOL_TYPE_TOUCHPAD = 4,
+ * UI_INPUT_EVENT_TOOL_TYPE_JOYSTICK = 5
+ */
+ int32_t toolType = OH_ArkUI_UIInputEvent_GetToolType(event);
+
+#if OHOS_SDK_VERSION >= 15
+ int32_t axisAction = 0;
+ if(QtOh::apiVersion() >= 15){
+ /* 获取当前轴事件的操作类型的值
+ * UI_AXIS_EVENT_ACTION_NONE = 0, // The axis event is abnormal.
+ * UI_AXIS_EVENT_ACTION_BEGIN = 1, // The axis event begins.
+ * UI_AXIS_EVENT_ACTION_UPDATE = 2, // The axis event is updated.
+ * UI_AXIS_EVENT_ACTION_END = 3, // The axis event ends.
+ * UI_AXIS_EVENT_ACTION_CANCEL = 4, // The axis event is canceled.
+ */
+ axisAction = OH_ArkUI_AxisEvent_GetAxisAction(event);
+
+ /* NOTE 鼠标滚轮只处理UPDATE? */
+ if (UI_INPUT_EVENT_TOOL_TYPE_MOUSE == toolType &&
+ UI_AXIS_EVENT_ACTION_UPDATE != axisAction) {
+ return;
+ }
+ }
+#endif
+
+#if OHOS_SDK_VERSION >= 17
+ int32_t scrollStep = qMax(1, OH_ArkUI_AxisEvent_GetScrollStep(event));
+#endif
+
+ /* 获取UI输入事件发生的时间 */
+ int64_t timestamp = OH_ArkUI_UIInputEvent_GetEventTime(event);
+
+ /* 获取产生UI输入事件的来源类型,鸿蒙端定义如下:
+ * UI_INPUT_EVENT_SOURCE_TYPE_UNKNOWN = 0,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_MOUSE = 1,
+ * UI_INPUT_EVENTT_SOURCE_TYPE_TOUCH_SCREEN = 2
+ */
+ int32_t sourceType = OH_ArkUI_UIInputEvent_GetSourceType(event);
+ double verticalValue = OH_ArkUI_AxisEvent_GetVerticalAxisValue(event);
+ double horizontalValue = OH_ArkUI_AxisEvent_GetHorizontalAxisValue(event);
+
+ if (QOhEventDispatcher *dispatcher = QOhEventDispatcher::instance()) {
+ QSharedPointer<QtOh::WheelEvent> e(new QtOh::WheelEvent());
+ e->pinchScale = scale;
+ e->toolType = toolType;
+ e->timestamp = timestamp;
+
+#if OHOS_SDK_VERSION >= 15
+ e->axisAction = axisAction;
+#endif
+
+#if OHOS_SDK_VERSION >= 17
+ e->scrollStep = scrollStep;
+#endif
+ e->sourceType = sourceType;
+ e->local = QPointF(lx, ly);
+ /* FIXME wanghao 多屏坐标错误的临时处理 */
+ e->window = windowFromXComponent(component);
+ e->global = e->window->handle()->mapToGlobal(e->local.toPoint());
+
+ e->verticalValue = verticalValue;
+ e->horizontalValue = horizontalValue;
+ dispatcher->appendOhEvent(e);
+ }
+}
+#endif
+
+void QOhXComponent::initXComponent(OH_NativeXComponent *component)
+{
+ if (m_nativeComponent != nullptr) {
+ /* 注册界面渲染事件回调 */
+ int32_t ret = OH_NativeXComponent_RegisterCallback(component, &m_componentCallback);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("set surface touch callback failed");
+ }
+
+ /* 注册获焦-失焦回调 */
+ ret = OH_NativeXComponent_RegisterFocusEventCallback(component, &QOhXComponent::onFocusInEvent);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGI("register focus in callback failed.");
+ }
+
+ ret = OH_NativeXComponent_RegisterBlurEventCallback(component, &QOhXComponent::onFocusOutEvent);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGI("register focus in callback failed.");
+ }
+#if QT_CONFIG(wheelevent)
+#if OHOS_SDK_VERSION < 17 // API17 使用node的回调
+ /* 注册UI输入事件回调,轴事件 */
+ ret = OH_NativeXComponent_RegisterUIInputEventCallback(component, &QOhXComponent::dispatchArkUIInputEvent,
+ ARKUI_UIINPUTEVENT_TYPE_AXIS);
+
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("set axis event callback failed");
+ }
+#endif
+#endif
+ // 如果API>=15,则使用多模鼠标事件
+ // 代码见QOhNormalWindow::windowMouseEventFilter
+ if (OHOS_SDK_VERSION < 15 || QtOh::isOpenHarmonyDevice()) {
+ /* 注册界鼠标事件回调 */
+ ret = OH_NativeXComponent_RegisterMouseEventCallback(component, &m_mouseEventCallback);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
+ LOGE("set surface mouse callback failed");
+ }
+ }
+
+#if OHOS_SDK_VERSION < 17 // API17 使用node的回调
+ /* 注册键盘事件回调 */
+ /*dispatchKeyEvent返回true时,该事件将不会继续分发(阻止tab键触发xc走焦)*/
+ ret = OH_NativeXComponent_RegisterKeyEventCallbackWithResult(component, &QOhXComponent::dispatchKeyEvent);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS)
+ LOGE("set surface keybord callback failed");
+#endif
+ ret = OH_NativeXComponent_RegisterSurfaceShowCallback(component, &QOhXComponent::onSurfaceShow);
+ if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS)
+ LOGE("set surface show callback failed");
+ }
+}
+
+OH_NativeXComponent *QOhXComponent::nativeComponent() const
+{
+ return m_nativeComponent;
+}
+
+void QOhXComponent::setNativeXComponent(OH_NativeXComponent *component)
+{
+ m_nativeComponent = component;
+ initXComponent(component);
+}
+
+void QOhXComponent::onFocusInEvent(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(component);
+ Q_UNUSED(window);
+}
+
+void QOhXComponent::onFocusOutEvent(OH_NativeXComponent *component, void *window)
+{
+ Q_UNUSED(component);
+ Q_UNUSED(window);
+}
+
+void QOhXComponent::setNativeWindow(void *nativeWindow)
+{
+ if (m_node == nullptr)
+ return;
+ m_node->setNativeWindow(nativeWindow);
+}
+
+void QOhXComponent::handleSurfaceChanged(const QSize &size)
+{
+ if (m_node == nullptr)
+ return;
+ m_node->handleSurfaceChanged(size);
+}
new file mode 100644
@@ -0,0 +1,70 @@
+#ifndef QOHXCOMPONENT_H
+#define QOHXCOMPONENT_H
+
+#include <QString>
+#include <QSize>
+#include <QMutex>
+#include <QtGui/qtguiglobal.h>
+#include <native_window/external_window.h>
+#include <ace/xcomponent/native_interface_xcomponent.h>
+#if QT_CONFIG(wheelevent)
+#include <arkui/ui_input_event.h>
+#endif
+
+class QOhWindowNode;
+class QWindow;
+struct OH_NativeXComponent;
+
+class QOhXComponent
+{
+public:
+ QOhXComponent(QOhWindowNode *node, OH_NativeXComponent *component = nullptr);
+ virtual ~QOhXComponent();
+
+ QString name() const;
+ OH_NativeXComponent *nativeComponent() const;
+
+ void setNativeWindow(void *nativeWindow);
+
+ void handleSurfaceChanged(const QSize &size);
+private:
+ void setNativeXComponent(OH_NativeXComponent *component);
+
+ static void onFocusInEvent(OH_NativeXComponent *component, void *window);
+ static void onFocusOutEvent(OH_NativeXComponent *component, void *window);
+ static void onSurfaceCreated(OH_NativeXComponent* component, void* window);
+
+ static void onSurfaceChanged(OH_NativeXComponent* component, void* window);
+
+ static void onSurfaceDestroyed(OH_NativeXComponent* component, void* window);
+
+ static void onSurfaceHided(OH_NativeXComponent *component, void *window);
+ static void onSurfaceShow(OH_NativeXComponent *component, void *window);
+
+ static bool dispatchKeyEvent(OH_NativeXComponent *component, void *window);
+
+ static void dispatchTouchEvent(OH_NativeXComponent* component, void* window);
+
+ static void dispatchMouseEvent(OH_NativeXComponent* component, void* window);
+ static void dispatchHoverEvent(OH_NativeXComponent *component, bool isHover);
+
+#if QT_CONFIG(wheelevent)
+ static void dispatchArkUIInputEvent(OH_NativeXComponent* component, ArkUI_UIInputEvent* event,
+ ArkUI_UIInputEvent_Type type);
+#endif
+
+private:
+ static QOhXComponent *find(OH_NativeXComponent *component);
+ static QString getName(OH_NativeXComponent *component);
+ static QWindow *windowFromXComponent(OH_NativeXComponent *component);
+ void initXComponent(OH_NativeXComponent *component);
+ void handleWindowStatusEvent(QtOh::WindowStatusType event);
+private:
+ static OH_NativeXComponent_Callback m_componentCallback;
+ static OH_NativeXComponent_MouseEvent_Callback m_mouseEventCallback;
+ static inline QList<QOhXComponent *> m_allXCompoents = {};
+ static QMutex m_compoentMutex;
+ OH_NativeXComponent *m_nativeComponent = nullptr;
+ QOhWindowNode *m_node;
+};
+#endif // QOHXCOMPONENT_H
@@ -1,6 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(OHOS)
+ add_subdirectory(openharmony)
+endif()
if(QT_FEATURE_cups AND UNIX AND NOT APPLE)
add_subdirectory(cups)
endif()
new file mode 100644
@@ -0,0 +1,24 @@
+qt_find_package(Cups PROVIDED_TARGETS Cups::Cups)
+#####################################################################
+## QOpenHarmonyPrinterSupportPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QOpenHarmonyPrinterSupportPlugin
+ OUTPUT_NAME openharmonyprintersupport
+ PLUGIN_TYPE printsupport
+ SOURCES
+ main.cpp
+ qopenharmonyprintdevice.cpp qopenharmonyprintdevice.h
+ qopenharmonyprintersupport.cpp qopenharmonyprintersupport_p.h
+ qopenharmonyprintengine.cpp qopenharmonyprintengine_p.h
+ INCLUDE_DIRECTORIES
+ ../../../printsupport/kernel
+ LIBRARIES
+ Qt::Core
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::PrintSupport
+ Qt::PrintSupportPrivate
+ ohprint ace_napi.z ohfileuri ace_ndk.z rawfile.z
+)
new file mode 100644
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include <qpa/qplatformprintplugin.h>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPrinterSupportPlugin : public QPlatformPrinterSupportPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformPrinterSupportFactoryInterface_iid FILE "openharmonyprint.json")
+
+public:
+ QStringList keys() const;
+ QPlatformPrinterSupport *create(const QString &) override;
+};
+
+QStringList QOhPrinterSupportPlugin::keys() const
+{
+ return QStringList(QStringLiteral("openharmonyprintersupport"));
+}
+
+QPlatformPrinterSupport *QOhPrinterSupportPlugin::create(const QString &key)
+{
+ if (key.compare(key, QLatin1String("openharmonyprintersupport"), Qt::CaseInsensitive) == 0)
+ return new QOpenHarmonyPrinterSupport;
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
new file mode 100644
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "openharmonyprintersupport" ]
+}
new file mode 100644
@@ -0,0 +1,541 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonyprintdevice.h"
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include <QDebug>
+
+#if QT_CONFIG(mimetype)
+#include <QtCore/QMimeDatabase>
+#endif
+
+QT_BEGIN_NAMESPACE
+#define PDPK_OHOSPrinterId QPrintDevice::PrintDevicePropertyKey(QPrintDevice::PDPK_CustomBase + 100)
+QOpenHarmonyPrintDevice::QOpenHarmonyPrintDevice(const QString &id)
+ : QPlatformPrintDevice(id),
+ m_printerInfo(new Print_PrinterInfo),
+ m_ppd(0)
+{
+ if (!id.isEmpty()) {
+ m_PrinterId = id.toUtf8();
+ // Get the print instance and PPD file
+ //鸿蒙native接口中没有ppd相关接口。
+ int ret = OH_Print_QueryPrinterInfo(m_PrinterId.constData(), &m_printerInfo);
+ if(PRINT_ERROR_NONE == ret){
+// m_id = m_printerInfo->printerId;
+ m_id = m_printerInfo->printerName;
+ m_name = m_printerInfo->printerName;
+ m_location = m_printerInfo->location;
+ m_makeAndModel = m_printerInfo->makeAndModel;
+ //鸿蒙native接口中没有下面几个信息,暂定为false。
+ m_isRemote = false;
+ m_supportsMultipleCopies = false;
+ m_supportsCollateCopies = false;
+ m_supportsCustomPageSizes = false;
+ }
+ else{
+ qWarning() << "QOpenHarmonyPrintDevice:query printer info fail,errCode:" << ret;
+ m_printerInfo = nullptr;
+ }
+ }else{
+ m_printerInfo = nullptr;
+ qWarning() << "QOpenHarmonyPrintDevice:device id is empty";
+ }
+}
+
+QOpenHarmonyPrintDevice::~QOpenHarmonyPrintDevice()
+{
+ if (m_ppd)
+ //鸿蒙native接口中没有ppd相关接口。
+ //ppdClose(m_ppd);
+ if (m_printerInfo)
+ OH_Print_ReleasePrinterInfo(m_printerInfo);
+ m_printerInfo = 0;
+ m_ppd = 0;
+}
+
+bool QOpenHarmonyPrintDevice::isValid() const
+{
+ return m_printerInfo;
+}
+
+bool QOpenHarmonyPrintDevice::isDefault() const
+{
+ return m_printerInfo->isDefaultPrinter;
+}
+
+QPrint::DeviceState QOpenHarmonyPrintDevice::state() const
+{
+ Print_PrinterState state = m_printerInfo ? m_printerInfo->printerState : PRINTER_UNAVAILABLE;
+ if (PRINTER_IDLE == state)
+ return QPrint::Idle;
+ else if (PRINTER_BUSY == state)
+ /* TODO 待确定PRINTER_BUSY对应Active还是Aborted */
+ return QPrint::Active;
+ else if (PRINTER_UNAVAILABLE == state)
+ return QPrint::Error;
+ else
+ return QPrint::Error;
+}
+
+extern qreal qt_pointMultiplier(QPageLayout::Unit unit);
+void QOpenHarmonyPrintDevice::loadPageSizes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_pageSizes.clear();
+ m_printableMargins.clear();
+
+ Print_PrinterCapability printerCapability = m_printerInfo->capability;
+ Print_Margin defaultMargin = m_printerInfo->defaultValue.defaultMargin;
+ uint32_t pageSizesCount = printerCapability.supportedPageSizesCount;
+ Print_PageSize *pageSize = printerCapability.supportedPageSizes;
+ static auto converToPoint = [](uint32_t value)->int {
+ const double factor = 25.4 * qt_pointMultiplier(QPageLayout::Millimeter) / 1000.0;
+ return qRound(value * factor);
+ };
+ static auto converToMillimeter = [](uint32_t value) {
+ return value / 1000.0;
+ };
+
+ for(uint32_t i = 0; i < pageSizesCount; ++i){
+ QString key = pageSize[i].id;
+ QSize size = QSize(converToPoint(pageSize[i].width), converToPoint(pageSize[i].height));
+ QString name = QString::fromLocal8Bit(pageSize[i].name);
+ if (!size.isEmpty()) {
+ QPageSize ps = createPageSize(key, size, name);
+ qWarning() << "create page size:" << ps << "original size:" << QSize(pageSize[i].width, pageSize[i].height);
+ if (ps.isValid()) {
+ m_pageSizes.append(ps);
+ m_printableMargins.insert(key, QMarginsF(converToMillimeter(defaultMargin.leftMargin),
+ converToMillimeter(defaultMargin.topMargin),
+ converToMillimeter(defaultMargin.rightMargin),
+ converToMillimeter(defaultMargin.bottomMargin)) );
+ }
+ }
+ }
+
+ m_havePageSizes = true;
+}
+
+QPageSize QOpenHarmonyPrintDevice::defaultPageSize() const
+{
+ if(!m_printerInfo)
+ return QPageSize();
+
+ Print_DefaultValue defaultVal = m_printerInfo->defaultValue;
+ QString key = QString::fromLocal8Bit(defaultVal.defaultPageSizeId);
+ auto it = std::find_if(m_pageSizes.constBegin(), m_pageSizes.constEnd(),[&key](const QPageSize &page){
+ return key == page.key();
+ });
+
+ return it == m_pageSizes.constEnd() ? QPageSize() : *it;
+}
+
+QMarginsF QOpenHarmonyPrintDevice::printableMargins(const QPageSize &pageSize,
+ QPageLayout::Orientation orientation,
+ int resolution) const
+{
+ Q_UNUSED(orientation)
+ Q_UNUSED(resolution)
+ if (!m_havePageSizes)
+ loadPageSizes();
+ // TODO Orientation?
+ if (m_printableMargins.contains(pageSize.key()))
+ return m_printableMargins.value(pageSize.key());
+ return m_customMargins;
+}
+
+void QOpenHarmonyPrintDevice::loadResolutions() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_resolutions.clear();
+
+ Print_Resolution *resolution = m_printerInfo->capability.supportedResolutions;
+ uint resolutionCount = m_printerInfo->capability.supportedResolutionsCount;
+
+ //Try load printer supportedResolutions
+ if(resolutionCount > 0){
+ for (int i = 0; i < resolutionCount; ++i) {
+ /* FIXME 暂定水平和垂直分辨率相同 */
+ if((resolution[i].horizontalDpi > 0) &&
+ (!m_resolutions.contains(resolution[i].horizontalDpi))){
+ m_resolutions.append(resolution[i].horizontalDpi);
+ }
+ }
+ }
+
+ // If no result, try just the default
+ if (m_resolutions.size() == 0) {
+ Print_Resolution defaultResolution = m_printerInfo->defaultValue.defaultResolution;
+ if((defaultResolution.horizontalDpi > 0) &&
+ (!m_resolutions.contains(defaultResolution.horizontalDpi))){
+ m_resolutions.append(defaultResolution.horizontalDpi);
+ }
+ }
+
+ m_haveResolutions = true;
+}
+
+int QOpenHarmonyPrintDevice::defaultResolution() const
+{
+ if(!m_printerInfo)
+ return 72;
+
+ Print_Resolution defaultResolution = m_printerInfo->defaultValue.defaultResolution;
+ int res = defaultResolution.horizontalDpi;
+ if(res > 0){
+ return res;
+ }
+ // Otherwise return a sensible default.
+ // TODO What is sensible? 150? 300?
+ return 72;
+}
+
+//todo:鸿蒙native接口中没有InputSlots信息
+void QOpenHarmonyPrintDevice::loadInputSlots() const
+{
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // TODO Deal with concatenated names like Tray1Manual or Tray1_Man,
+ // // will currently show as CustomInputSlot
+ // // TODO Deal with separate ManualFeed key
+ // // Try load standard PPD options first
+ // m_inputSlots.clear();
+ // if (m_ppd) {
+ // ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot");
+ // if (inputSlots) {
+ // m_inputSlots.reserve(inputSlots->num_choices);
+ // for (int i = 0; i < inputSlots->num_choices; ++i)
+ // m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i]));
+ // }
+ // // If no result, try just the default
+ // if (m_inputSlots.size() == 0) {
+ // inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot");
+ // if (inputSlots)
+ // m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0]));
+ // }
+ // }
+ // // If still no result, just use Auto
+ // if (m_inputSlots.size() == 0)
+ // m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot());
+ // m_haveInputSlots = true;
+}
+
+QPrint::InputSlot QOpenHarmonyPrintDevice::defaultInputSlot() const
+{
+ //todo:鸿蒙native接口中没有InputSlots信息
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // Try load standard PPD option first
+ // if (m_ppd) {
+ // ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot");
+ // if (inputSlot)
+ // return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]);
+ // // If no result, then try a marked option
+ // ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot");
+ // if (defaultChoice)
+ // return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice);
+ // }
+ // Otherwise return Auto
+ return QPlatformPrintDevice::defaultInputSlot();
+}
+//todo:鸿蒙native接口中没有OutputBins信息
+void QOpenHarmonyPrintDevice::loadOutputBins() const
+{
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // m_outputBins.clear();
+ // if (m_ppd) {
+ // ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin");
+ // if (outputBins) {
+ // m_outputBins.reserve(outputBins->num_choices);
+ // for (int i = 0; i < outputBins->num_choices; ++i)
+ // m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i]));
+ // }
+ // // If no result, try just the default
+ // if (m_outputBins.size() == 0) {
+ // outputBins = ppdFindOption(m_ppd, "DefaultOutputBin");
+ // if (outputBins)
+ // m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0]));
+ // }
+ // }
+ // // If still no result, just use Auto
+ // if (m_outputBins.size() == 0)
+ // m_outputBins.append(QPlatformPrintDevice::defaultOutputBin());
+ // m_haveOutputBins = true;
+}
+
+QPrint::OutputBin QOpenHarmonyPrintDevice::defaultOutputBin() const
+{
+ //todo:鸿蒙native接口中没有OutputBins信息
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // Try load standard PPD option first
+ // if (m_ppd) {
+ // ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin");
+ // if (outputBin)
+ // return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]);
+ // // If no result, then try a marked option
+ // ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin");
+ // if (defaultChoice)
+ // return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice);
+ // }
+ // Otherwise return AutoBin
+ return QPlatformPrintDevice::defaultOutputBin();
+}
+
+void QOpenHarmonyPrintDevice::loadDuplexModes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ // Try load standard PPD options first
+ m_duplexModes.clear();
+ Print_DuplexMode *duplexModes = m_printerInfo->capability.supportedDuplexModes;
+ uint duplexModesCount = m_printerInfo->capability.supportedDuplexModesCount;
+ QPrint::DuplexMode qtDuplexMode;
+
+ if(duplexModes){
+ m_duplexModes.reserve(duplexModesCount);
+ for (int i = 0; i < duplexModesCount; ++i) {
+ switch(*(duplexModes+i)){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ if(!m_duplexModes.contains(qtDuplexMode)){
+ m_duplexModes.append(qtDuplexMode);
+ }
+ }
+ }
+
+ if (m_duplexModes.size() == 0) {
+ Print_DuplexMode defaultDuplexMode = m_printerInfo->defaultValue.defaultDuplexMode;
+ switch(defaultDuplexMode){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ if(!m_duplexModes.contains(qtDuplexMode)){
+ m_duplexModes.append(qtDuplexMode);
+ }
+ }
+
+ // If still no result, or not added in PPD, then add None
+ if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone))
+ m_duplexModes.append(QPrint::DuplexNone);
+ // If have both modes, then can support DuplexAuto
+ if (m_duplexModes.contains(QPrint::DuplexLongSide) && m_duplexModes.contains(QPrint::DuplexShortSide))
+ m_duplexModes.append(QPrint::DuplexAuto);
+
+ m_haveDuplexModes = true;
+}
+
+QPrint::DuplexMode QOpenHarmonyPrintDevice::defaultDuplexMode() const
+{
+ QPrint::DuplexMode qtDuplexMode;
+ Print_DuplexMode defaultDuplexMode = m_printerInfo ? m_printerInfo->defaultValue.defaultDuplexMode : DUPLEX_MODE_ONE_SIDED;
+ switch(defaultDuplexMode){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ return qtDuplexMode;
+}
+
+void QOpenHarmonyPrintDevice::loadColorModes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_colorModes.clear();
+
+ Print_ColorMode *colorModes = m_printerInfo->capability.supportedColorModes;
+ uint colorModesCount = m_printerInfo->capability.supportedColorModesCount;
+ QPrint::ColorMode qtColorMode;
+
+ for (int i = 0; i < colorModesCount; ++i) {
+ switch(*(colorModes+i)){
+ case COLOR_MODE_MONOCHROME:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ case COLOR_MODE_COLOR:
+ qtColorMode = QPrint::Color;
+ break;
+ case COLOR_MODE_AUTO:
+ qtColorMode = QPrint::Color;
+ break;
+ default:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ }
+ if(!m_colorModes.contains(qtColorMode)){
+ m_colorModes.append(qtColorMode);
+ }
+ }
+
+ m_haveColorModes = true;
+}
+
+QPrint::ColorMode QOpenHarmonyPrintDevice::defaultColorMode() const
+{
+ // Not a proper option, usually only know if supports color or not, but some
+ // users known to abuse ColorModel to always force GrayScale.
+ if (supportedColorModes().contains(QPrint::Color)) {
+ QPrint::ColorMode qtColorMode = QPrint::GrayScale;
+
+ if(m_printerInfo && m_printerInfo->printerId != nullptr){
+ Print_ColorMode defaultColorMode = m_printerInfo->defaultValue.defaultColorMode;
+ switch(defaultColorMode){
+ case COLOR_MODE_MONOCHROME:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ case COLOR_MODE_COLOR:
+ qtColorMode = QPrint::Color;
+ break;
+ case COLOR_MODE_AUTO:
+ qtColorMode = QPrint::Color;
+ break;
+ default:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ }
+ }
+
+ if(QPrint::Color == qtColorMode)
+ {
+ return qtColorMode;
+ }
+ }
+
+ return QPrint::GrayScale;
+}
+
+QVariant QOpenHarmonyPrintDevice::property(QPrintDevice::PrintDevicePropertyKey key) const
+{
+ //todo:鸿蒙native没有这些接口
+ // if (key == PDPK_PpdFile)
+ // return QVariant::fromValue<ppd_file_t *>(m_ppd);
+ // else if (key == PDPK_CupsJobPriority)
+ // return printerOption(QStringLiteral("job-priority"));
+ // else if (key == PDPK_CupsJobSheets)
+ // return printerOption(QStringLiteral("job-sheets"));
+ // else if (key == PDPK_CupsJobBilling)
+ // return printerOption(QStringLiteral("job-billing"));
+ // else if (key == PDPK_CupsJobHoldUntil)
+ // return printerOption(QStringLiteral("job-hold-until"));
+ if (key == PDPK_OHOSPrinterId) {
+ return m_PrinterId;
+ }
+
+ return QPlatformPrintDevice::property(key);
+}
+
+bool QOpenHarmonyPrintDevice::setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value)
+{
+ //todo:鸿蒙native没有该接口
+ // if (key == PDPK_PpdOption) {
+ // const QStringList values = value.toStringList();
+ // if (values.count() == 2) {
+ // ppdMarkOption(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ // return true;
+ // }
+ // }
+
+ return QPlatformPrintDevice::setProperty(key, value);
+}
+
+bool QOpenHarmonyPrintDevice::isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant ¶ms) const
+{
+ //todo:鸿蒙native没有该接口
+ // if (key == PDPK_PpdChoiceIsInstallableConflict) {
+ // const QStringList values = params.toStringList();
+ // if (values.count() == 2)
+ // return ppdInstallableConflict(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ // }
+
+ return QPlatformPrintDevice::isFeatureAvailable(key, params);
+}
+
+#if QT_CONFIG(mimetype)
+void QOpenHarmonyPrintDevice::loadMimeTypes() const
+{
+ QMimeDatabase db;
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/pdf")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/postscript")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/gif")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/png")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/jpeg")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/tiff")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/html")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/plain")));
+ m_haveMimeTypes = true;
+}
+#endif
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOpenHarmonyPrintDevice_H
+#define QOpenHarmonyPrintDevice_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of internal files. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtPrintSupport/qprintengine.h"
+#include <qpa/qplatformprintdevice.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmargins.h>
+#include <BasicServicesKit/ohprint.h>
+
+QT_BEGIN_NAMESPACE
+
+struct oh_ppd_file_t; //todo:仅占位,鸿蒙native接口没有该结构体。
+
+class QOpenHarmonyPrintDevice : public QPlatformPrintDevice
+{
+public:
+ explicit QOpenHarmonyPrintDevice(const QString &id);
+ virtual ~QOpenHarmonyPrintDevice();
+
+ bool isValid() const override;
+ bool isDefault() const override;
+
+ QPrint::DeviceState state() const override;
+
+ QPageSize defaultPageSize() const override;
+
+ QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation,
+ int resolution) const override;
+
+ int defaultResolution() const override;
+
+ QPrint::InputSlot defaultInputSlot() const override;
+
+ QPrint::OutputBin defaultOutputBin() const override;
+
+ QPrint::DuplexMode defaultDuplexMode() const override;
+
+ QPrint::ColorMode defaultColorMode() const override;
+
+ QVariant property(QPrintDevice::PrintDevicePropertyKey key) const override;
+ bool setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value) override;
+ bool isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant ¶ms) const override;
+
+protected:
+ void loadPageSizes() const override;
+ void loadResolutions() const override;
+ void loadInputSlots() const override;
+ void loadOutputBins() const override;
+ void loadDuplexModes() const override;
+ void loadColorModes() const override;
+#if QT_CONFIG(mimetype)
+ void loadMimeTypes() const override;
+#endif
+
+private:
+ Print_PrinterInfo *m_printerInfo;
+ oh_ppd_file_t *m_ppd;
+ QByteArray m_PrinterId;
+ QMarginsF m_customMargins;
+ mutable QHash<QString, QMarginsF> m_printableMargins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOpenHarmonyPrintDevice_H
new file mode 100644
@@ -0,0 +1,466 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <qfile.h>
+#include <qdebug.h>
+#include <qbuffer.h>
+#include <QFileInfo>
+#include <qiodevice.h>
+#include <QPrintDialog>
+#include <QStandardPaths>
+#include <QtGui/qpagelayout.h>
+
+#include <qpa/qplatformprintplugin.h>
+#include <BasicServicesKit/ohprint.h>
+#include <qpa/qplatformprintersupport.h>
+
+#include "private/qcore_unix_p.h" // overrides QT_OPEN
+#include "qopenharmonyprintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+#define PDPK_OHOSPrinterId QPrintDevice::PrintDevicePropertyKey(QPrintDevice::PDPK_CustomBase + 100)
+extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits);
+
+QOpenHarmonyPrintEngine::QOpenHarmonyPrintEngine(QPrinter::PrinterMode m, const QString &deviceId)
+ : QPdfPrintEngine(*new QOpenHarmonyPrintEnginePrivate(m))
+{
+ Q_D(QOpenHarmonyPrintEngine);
+ d->changePrinter(deviceId);
+ state = QPrinter::Idle;
+}
+
+QOpenHarmonyPrintEngine::~QOpenHarmonyPrintEngine()
+{
+}
+
+void QOpenHarmonyPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QOpenHarmonyPrintEngine);
+
+ switch (int(key)) {
+ case PPK_PageSize:
+ d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt())));
+ break;
+ case PPK_WindowsPageSize:
+ d->setPageSize(QPageSize(QPageSize::id(value.toInt())));
+ break;
+ case PPK_CustomPaperSize:
+ d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point));
+ break;
+ case PPK_PaperName:
+ // Get the named page size from the printer if supported
+ d->setPageSize(d->m_printDevice.supportedPageSize(value.toString()));
+ break;
+ case PPK_Duplex: {
+ QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt());
+ if (d->m_printDevice.supportedDuplexModes().contains(mode)) {
+ d->duplex = mode;
+ d->duplexRequestedExplicitly = true;
+ }
+ break;
+ }
+ case PPK_Orientation: {
+ d->m_pageLayout.setOrientation(QPageLayout::Orientation(value.toInt()));
+ qWarning() << "set ppk orientation:" << d->m_pageLayout;
+ }
+ break;
+ case PPK_PrinterName:
+ d->changePrinter(value.toString());
+ break;
+ case PPK_OhPrintOptions:
+ d->ohOptions = value.toStringList();
+ break;
+ case PPK_QPageSize:
+ d->setPageSize(qvariant_cast<QPageSize>(value));
+ break;
+ case PPK_QPageLayout: {
+ QPageLayout pageLayout = qvariant_cast<QPageLayout>(value);
+ d->m_pageLayout = pageLayout;
+ qWarning() << "set ppk page layout:" << d->m_pageLayout;
+#if 0
+ if (pageLayout.isValid() && (d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)
+ || d->m_printDevice.supportsCustomPageSizes()
+ || d->m_printDevice.supportedPageSizes().isEmpty())) {
+ // supportedPageSizes().isEmpty() because QPageSetupWidget::initPageSizes says
+ // "If no available printer page sizes, populate with all page sizes"
+ d->m_pageLayout = pageLayout;
+ d->setPageSize(pageLayout.pageSize());
+ }
+#endif
+ break;
+ }
+ default:
+ QPdfPrintEngine::setProperty(key, value);
+ break;
+ }
+}
+
+QVariant QOpenHarmonyPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QOpenHarmonyPrintEngine);
+
+ QVariant ret;
+ switch (int(key)) {
+ case PPK_SupportsMultipleCopies:
+ //不确定是否支持该属性,暂时认为不支持。
+ ret = false;
+ break;
+ case PPK_NumberOfCopies:
+ ret = 1;
+ break;
+ case PPK_OhPrintOptions:
+ ret = d->ohOptions;
+ break;
+ case PPK_Duplex:
+ ret = d->duplex;
+ break;
+ default:
+ ret = QPdfPrintEngine::property(key);
+ break;
+ }
+ return ret;
+}
+
+
+QOpenHarmonyPrintEnginePrivate::QOpenHarmonyPrintEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfPrintEnginePrivate(m)
+ , duplex(QPrint::DuplexNone)
+ , printMode(m)
+{
+ outputFileName.clear();
+ ownsDevice = true; /* FIXME 是否让PDF基类控制文件内容落盘 */
+}
+
+QOpenHarmonyPrintEnginePrivate::~QOpenHarmonyPrintEnginePrivate()
+{
+}
+
+bool QOpenHarmonyPrintEnginePrivate::openPrintDevice()
+{
+ if (this->outDevice)
+ return false;
+
+ if (!outputFileName.isEmpty()) {
+ QFile *file = new QFile(outputFileName);
+ if (!file->open(QFile::WriteOnly|QFile::Truncate)) {
+ delete file;
+ return false;
+ }
+ this->outDevice = file;
+ } else {
+ QUuid uuid = QUuid::createUuid();
+ const QString &tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ ohTempFile = QString("%1/%2.pdf").arg(tempDir, uuid.toString(QUuid::Id128));
+ fd = open(ohTempFile.toLocal8Bit().constData(), O_CREAT | O_SYNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP);
+ QFile *f = new QFile();
+ // if (!f->open(QIODevice::WriteOnly)) {
+ // f->close();
+ // qWarning() << "create print temp file failed:" << f->errorString();
+ // return false;
+ // }
+ this->outDevice = f;
+ static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
+ //this->fd = f->handle();
+ qWarning() << "open tmp file:" << ohTempFile << "fd:" << fd << outDevice;
+ }
+
+ return true;
+}
+
+void QOpenHarmonyPrintEnginePrivate::closePrintDevice()
+{
+ Q_Q(QOpenHarmonyPrintEngine);
+ if (!ohTempFile.isEmpty()) {
+ QString tempFile = ohTempFile;
+ ohTempFile.clear();
+
+ // Should never have got here without a printer, but check anyway
+ if (printerName.isEmpty()) {
+ qWarning("Could not determine printer to print to");
+ QFile::remove(tempFile);
+ return;
+ }
+
+ // Set up print options.
+ QList<QPair<QByteArray, QByteArray> > options;
+ QVector<Print_Property> ohPrintOptStruct;
+
+
+ options.append(QPair<QByteArray, QByteArray>("media", m_pageLayout.pageSize().key().toLocal8Bit()));
+
+ if (copies > 1)
+ options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
+
+ if (copies > 1 && collate)
+ options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
+
+ switch (duplex) {
+ case QPrint::DuplexNone:
+ options.append(QPair<QByteArray, QByteArray>("sides", "one-sided"));
+ break;
+ case QPrint::DuplexAuto:
+ if (m_pageLayout.orientation() == QPageLayout::Portrait)
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ else
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ case QPrint::DuplexLongSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ break;
+ case QPrint::DuplexShortSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ }
+
+ if (m_pageLayout.orientation() == QPageLayout::Landscape)
+ options.append(QPair<QByteArray, QByteArray>("landscape", ""));
+
+ QStringList::const_iterator it = ohOptions.constBegin();
+ while (it != ohOptions.constEnd()) {
+ options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
+ it += 2;
+ }
+
+ const int numOptions = options.size();
+ ohPrintOptStruct.reserve(numOptions);
+ for (int c = 0; c < numOptions; ++c) {
+ Print_Property prop;
+ prop.key = options[c].first.data();
+ prop.value = options[c].second.data();
+ ohPrintOptStruct.append(prop);
+ }
+
+ Print_PrintJob printJob;
+ fd = open(tempFile.toLocal8Bit().constData(), O_RDONLY);
+
+ QString jobName = title.isEmpty() ? QUuid::createUuid().toString(QUuid::Id128) : title;
+ QByteArray jobNameArray = jobName.toUtf8();
+ printJob.jobName = jobNameArray.data();
+ uint32_t fds[1] = { static_cast<uint32_t>(fd) };
+ printJob.fdList = fds;
+ printJob.fdList[0] = fd;
+ printJob.fdListCount = 1;
+
+ QByteArray printerId = printerName.toUtf8();
+ printJob.printerId = printerId.data();
+ printJob.copyNumber = copies;
+ // printJob.paperSource = q->property(PPK_PaperSources);
+ std::string paperSource = "auto";
+ printJob.paperSource = paperSource.data();
+ std::string mediaType = "stationery";
+ printJob.mediaType = mediaType.data();
+ QString key = m_pageLayout.pageSize().key();
+ QByteArray keyArray = key.toUtf8();
+ printJob.pageSizeId = keyArray.data();
+ //printJob.pageSizeId = "ISO_A4";
+ //Print_ColorMode
+ Print_ColorMode ohColorMode;
+ QPrinter::ColorMode qtColorMode = (QPrinter::ColorMode)q->property(QPrintEngine::PPK_ColorMode).toInt();
+ switch(qtColorMode){
+ case QPrinter::GrayScale:
+ ohColorMode = COLOR_MODE_MONOCHROME;
+ break;
+ case QPrinter::Color:
+ ohColorMode = COLOR_MODE_COLOR;
+ break;
+ default:
+ ohColorMode = COLOR_MODE_MONOCHROME;
+ break;
+ }
+ printJob.colorMode = ohColorMode;
+
+
+ //Print_DuplexMode
+ Print_DuplexMode ohDuplexMode;
+ QPrint::DuplexMode qtDuplexMode = (QPrint::DuplexMode)q->property(QPrintEngine::PPK_Duplex).toInt();
+ switch(qtDuplexMode){
+ case QPrint::DuplexNone:
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ case QPrint::DuplexAuto:
+ //自动模式暂时设置为One sided。
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ case QPrint::DuplexLongSide:
+ ohDuplexMode = DUPLEX_MODE_TWO_SIDED_LONG_EDGE;
+ break;
+ case QPrint::DuplexShortSide:
+ ohDuplexMode = DUPLEX_MODE_TWO_SIDED_SHORT_EDGE;
+ break;
+ default:
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ }
+ printJob.duplexMode = ohDuplexMode;
+
+ //Print_Resolution
+ int resolution = 600/*q->property(QPrintEngine::PPK_Resolution).toInt()*/;
+ printJob.resolution.horizontalDpi = resolution;
+ printJob.resolution.verticalDpi = resolution;
+
+ //Print_Margin
+ Print_Margin ohPrintMargin;
+ QPair<QMarginsF, QPageLayout::Unit> pair = qvariant_cast<QPair<QMarginsF, QPageLayout::Unit> >(q->property(QPrintEngine::PPK_QPageMargins));
+ ohPrintMargin.leftMargin = static_cast<uint>(pair.first.left());
+ ohPrintMargin.topMargin = static_cast<uint>(pair.first.top());
+ ohPrintMargin.rightMargin = static_cast<uint>(pair.first.right());
+ ohPrintMargin.bottomMargin = static_cast<uint>(pair.first.bottom());
+ printJob.printMargin = ohPrintMargin;
+ /* 默认无border */
+ printJob.borderless = false;
+
+ //Print_OrientationMode
+ Print_OrientationMode ohOrientationMode;
+ // int ohOrientationMode;
+ QPageLayout::Orientation qtOrientation = m_pageLayout.orientation();
+ switch(qtOrientation){
+ case QPageLayout::Portrait:
+ ohOrientationMode = ORIENTATION_MODE_PORTRAIT;
+ break;
+ case QPageLayout::Landscape:
+ ohOrientationMode = ORIENTATION_MODE_LANDSCAPE;
+ break;
+ default:
+ ohOrientationMode = ORIENTATION_MODE_PORTRAIT;
+ break;
+ }
+ printJob.orientationMode = ohOrientationMode;
+
+ //Print_Quality
+ Print_Quality ohQuality;
+ switch(printMode){
+ case QPrinter::ScreenResolution:
+ ohQuality = PRINT_QUALITY_DRAFT;
+ break;
+ case QPrinter::PrinterResolution:
+ ohQuality = PRINT_QUALITY_NORMAL;
+ break;
+ case QPrinter::HighResolution:
+ ohQuality = PRINT_QUALITY_HIGH;
+ break;
+ default:
+ ohQuality = PRINT_QUALITY_HIGH;
+ break;
+ }
+ printJob.printQuality = ohQuality;
+
+
+ //Print_DocumentFormat
+ Print_DocumentFormat ohDocumentFormat;
+ QFileInfo fi(outputFileName);
+ if (!fi.suffix().compare(QLatin1String("pdf"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_PDF;
+ else if (!fi.suffix().compare(QLatin1String("jpeg"), Qt::CaseInsensitive) ||
+ !fi.suffix().compare(QLatin1String("jpg"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_JPEG;
+ else if (!fi.suffix().compare(QLatin1String("ps"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_POSTSCRIPT;
+ else if (!fi.suffix().compare(QLatin1String("txt"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_TEXT;
+ else{
+ ohDocumentFormat = DOCUMENT_FORMAT_AUTO;
+ }
+ /* FIXME 暂时支持PDF格式 */
+ printJob.documentFormat = DOCUMENT_FORMAT_PDF;
+ printJob.advancedOptions = nullptr;
+
+ qWarning() << "start print job page layout" << this->m_pageLayout;
+ /* 启动打印任务 */
+ int ret = OH_Print_StartPrintJob(&printJob);
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "print job execute fail,errCode:" << ret;
+ }
+
+ //QPdfPrintEnginePrivate::closePrintDevice();
+ }
+}
+
+void QOpenHarmonyPrintEnginePrivate::changePrinter(const QString &newPrinter)
+{
+ // Don't waste time if same printer name
+ if (newPrinter == printerName)
+ return;
+
+ // Should never have reached here if no plugin available, but check just in case
+ QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
+ if (!ps)
+ return;
+
+ // Try create the printer, only use it if it returns valid
+ QPrintDevice printDevice = ps->createPrintDevice(newPrinter);
+ if (!printDevice.isValid())
+ return;
+ m_printDevice.swap(printDevice);
+ printerName = m_printDevice.property(PDPK_OHOSPrinterId).toString();
+
+ // in case a duplex value was explicitly set, check if new printer supports current value,
+ // otherwise use device default
+ if (!duplexRequestedExplicitly || !m_printDevice.supportedDuplexModes().contains(duplex)) {
+ duplex = m_printDevice.defaultDuplexMode();
+ duplexRequestedExplicitly = false;
+ }
+ QPrint::ColorMode colorMode = grayscale ? QPrint::GrayScale : QPrint::Color;
+ if (!m_printDevice.supportedColorModes().contains(colorMode))
+ grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale;
+
+ // Get the equivalent page size for this printer as supported names may be different
+ if (m_printDevice.supportedPageSize(m_pageLayout.pageSize()).isValid()) {
+ setPageSize(m_pageLayout.pageSize());
+ }
+ else {
+ setPageSize(QPageSize(m_pageLayout.pageSize().size(QPageSize::Point), QPageSize::Point));
+ }
+}
+
+void QOpenHarmonyPrintEnginePrivate::setPageSize(const QPageSize &pageSize)
+{
+ if (pageSize.isValid()) {
+ // Find if the requested page size has a matching printer page size, if so use its defined name instead
+ QPageSize printerPageSize = m_printDevice.supportedPageSize(pageSize);
+ QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize;
+ QMarginsF printable = m_printDevice.printableMargins(usePageSize, m_pageLayout.orientation(), resolution);
+ m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units()));
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYPRINTENGINE_P_H
+#define QOPENHARMONYPRINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtPrintSupport/qprintengine.h"
+
+#include <QtCore/qstring.h>
+#include <QtGui/qpaintengine.h>
+
+#include <private/qpaintengine_p.h>
+#include <private/qprintdevice_p.h>
+#include <private/qprintengine_pdf_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define PPK_OhPrintOptions QPrintEngine::PrintEnginePropertyKey(0xef55)
+
+class QOpenHarmonyPrintEnginePrivate;
+
+class QOpenHarmonyPrintEngine : public QPdfPrintEngine
+{
+ Q_DECLARE_PRIVATE(QOpenHarmonyPrintEngine)
+public:
+ QOpenHarmonyPrintEngine(QPrinter::PrinterMode m, const QString &deviceId);
+ virtual ~QOpenHarmonyPrintEngine();
+
+ // reimplementations QPdfPrintEngine
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value) override;
+ QVariant property(PrintEnginePropertyKey key) const override;
+ // end reimplementations QPdfPrintEngine
+
+private:
+ Q_DISABLE_COPY_MOVE(QOpenHarmonyPrintEngine)
+};
+
+class QOpenHarmonyPrintEnginePrivate : public QPdfPrintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QOpenHarmonyPrintEngine)
+public:
+ QOpenHarmonyPrintEnginePrivate(QPrinter::PrinterMode m);
+ ~QOpenHarmonyPrintEnginePrivate();
+
+ bool openPrintDevice() override;
+ void closePrintDevice() override;
+
+private:
+ Q_DISABLE_COPY_MOVE(QOpenHarmonyPrintEnginePrivate)
+
+ void changePrinter(const QString &newPrinter);
+ void setPageSize(const QPageSize &pageSize);
+
+ QPrintDevice m_printDevice;
+ QStringList ohOptions;
+ QString ohTempFile;
+ QPrint::DuplexMode duplex;
+ bool duplexRequestedExplicitly = false;
+ QPrinter::PrinterMode printMode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYPRINTENGINE_P_H
new file mode 100644
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include "qopenharmonyprintengine_p.h"
+#include "qopenharmonyprintdevice.h"
+#include <private/qprinterinfo_p.h>
+#include <private/qprintdevice_p.h>
+#include <QtPrintSupport/QPrinterInfo>
+
+#if QT_CONFIG(dialogbuttonbox)
+#include <QGuiApplication>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QLabel>
+#include <QLineEdit>
+#endif // QT_CONFIG(dialogbuttonbox)
+
+QT_BEGIN_NAMESPACE
+#if 0
+#if QT_CONFIG(dialogbuttonbox)
+static const char *getPasswordCB(const char */*prompt*/, http_t *http, const char */*method*/, const char *resource, void */*user_data*/)
+{
+ // cups doesn't free the const char * we return so keep around
+ // the last password so we don't leak memory if called multiple times.
+ static QByteArray password;
+
+ // prompt is always "Password for %s on %s? " but we can't use it since we allow the user to change the user.
+ // That is fine because cups always calls cupsUser after calling this callback.
+ // We build our own prompt with the hostname (if not localhost) and the resource that is being used
+
+ char hostname[HTTP_MAX_HOST];
+ httpGetHostname(http, hostname, HTTP_MAX_HOST);
+
+ const QString username = QString::fromLocal8Bit(cupsUser());
+
+ QDialog dialog;
+ dialog.setWindowTitle(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication Needed"));
+
+ QFormLayout *layout = new QFormLayout(&dialog);
+ layout->setSizeConstraint(QLayout::SetFixedSize);
+
+ QLineEdit *usernameLE = new QLineEdit();
+ usernameLE->setText(username);
+
+ QLineEdit *passwordLE = new QLineEdit();
+ passwordLE->setEchoMode(QLineEdit::Password);
+
+ QString resourceString = QString::fromLocal8Bit(resource);
+ if (resourceString.startsWith(QStringLiteral("/printers/")))
+ resourceString = resourceString.mid(QStringLiteral("/printers/").length());
+
+ QLabel *label = new QLabel();
+ if (hostname == QStringLiteral("localhost")) {
+ label->setText(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication needed to use %1.").arg(resourceString));
+ } else {
+ label->setText(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication needed to use %1 on %2.").arg(resourceString).arg(hostname));
+ label->setWordWrap(true);
+ }
+
+ layout->addRow(label);
+ layout->addRow(new QLabel(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Username:")), usernameLE);
+ layout->addRow(new QLabel(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Password:")), passwordLE);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ layout->addRow(buttonBox);
+
+ QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+ QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+
+ passwordLE->setFocus();
+
+ if (dialog.exec() != QDialog::Accepted)
+ return nullptr;
+
+ if (usernameLE->text() != username)
+ cupsSetUser(usernameLE->text().toLocal8Bit().constData());
+
+ password = passwordLE->text().toLocal8Bit();
+
+ return password.constData();
+}
+#endif // QT_CONFIG(dialogbuttonbox)
+#endif
+
+static QOpenHarmonyPrinterSupport *ps = nullptr;
+
+QOpenHarmonyPrinterSupport::QOpenHarmonyPrinterSupport()
+ : QPlatformPrinterSupport()
+{
+ ps = this;
+ Print_ErrorCode ret = Print_ErrorCode::PRINT_ERROR_NONE;
+
+ ret = OH_Print_Init();
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "QOpenHarmonyPrinterSupport:init Env failed,errCode:" << ret;
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:init Env ok";
+
+ ret = OH_Print_RegisterPrinterChangeListener(Print_PrinterChange);
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "QOpenHarmonyPrinterSupport:register printer change failed,errCode:" << ret;
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:register printer change ok";
+
+ ret = OH_Print_StartPrinterDiscovery(Print_PrinterDiscovery);
+ if (PRINT_ERROR_NONE != ret)
+ {
+ qWarning() << "QOpenHarmonyPrinterSupport:StartPrinterDiscovery failed,errCode:" << ret;
+ OH_Print_UnregisterPrinterChangeListener();
+ OH_Print_Release();
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:StartPrinterDiscovery ok";
+
+ detectOHPrinter();
+}
+
+QOpenHarmonyPrinterSupport::~QOpenHarmonyPrinterSupport()
+{
+ OH_Print_StopPrinterDiscovery();
+ OH_Print_UnregisterPrinterChangeListener();
+ OH_Print_Release();
+ ps = nullptr;
+}
+
+QPrintEngine *QOpenHarmonyPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId)
+{
+ return new QOpenHarmonyPrintEngine(printerMode, (deviceId.isEmpty() ? defaultPrintDeviceId() : deviceId));
+}
+
+QPaintEngine *QOpenHarmonyPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrinter::PrinterMode printerMode)
+{
+ Q_UNUSED(printerMode)
+ return static_cast<QOpenHarmonyPrintEngine *>(engine);
+}
+
+QPrintDevice QOpenHarmonyPrinterSupport::createPrintDevice(const QString &id)
+{
+ return QPlatformPrinterSupport::createPrintDevice(new QOpenHarmonyPrintDevice(idFromName(id)));
+}
+
+void QOpenHarmonyPrinterSupport::Print_PrinterChange(Print_PrinterEvent event, const Print_PrinterInfo *printerInfo)
+{
+ qInfo() << "Print_PrinterChange id:" << printerInfo->printerId << " name: " << printerInfo->printerName;
+
+#if 0
+ QString id = QString::fromLocal8Bit(printerInfo->printerId);
+ if (!ps)
+ return;
+ OHPrinter p;
+ p.id = id;
+ p.name = QString::fromLocal8Bit(printerInfo->printerName);
+ p.isDefault = printerInfo->isDefaultPrinter;
+
+ if(PRINTER_ADDED == event){
+ ps->add(p);
+ }else if(PRINTER_DELETED == event){
+ ps->deletePrinter(p);
+ }
+#endif
+}
+
+void QOpenHarmonyPrinterSupport::Print_PrinterDiscovery(Print_DiscoveryEvent event, const Print_PrinterInfo *printerInfo)
+{
+ qInfo() << "PrinterDiscovery id:" << printerInfo->printerId << " name: " << printerInfo->printerName;
+ QString id = QString::fromUtf8(printerInfo->printerId);
+ if (event == PRINTER_DISCOVERED) {
+ if (!ps || ps->contains(id))
+ return;
+ OHPrinter p;
+ p.id = id;
+ p.name = QString::fromUtf8(printerInfo->printerName);
+ p.isDefault = printerInfo->isDefaultPrinter;
+ ps->add(p);
+ } else if (event == PRINTER_LOST) {
+ if (ps != nullptr)
+ ps->remove(id);
+ }
+}
+
+void QOpenHarmonyPrinterSupport::detectOHPrinter()
+{
+ Print_StringList printerIdList;
+ int ret = OH_Print_QueryPrinterList(&printerIdList);
+
+ if (PRINT_ERROR_NONE != ret) {
+ qWarning() << "QOpenHarmonyPrinterSupport:detectOHPrinter OH_Print_QueryPrinterList failed,errCode:" << ret;
+ return;
+ }
+ for (int i = 0; i < printerIdList.count; ++i) {
+ char *id = printerIdList.list[i];
+ QString idString = QString::fromUtf8(id);
+ if (contains(idString)) {
+ continue;
+ }
+ Print_PrinterInfo *info = nullptr;
+ ret = OH_Print_QueryPrinterInfo(id, &info);
+ if (PRINT_ERROR_NONE != ret || info == nullptr) {
+ qWarning() << "QOpenHarmonyPrinterSupport:detectOHPrinter OH_Print_QueryPrinterInfo failed,errCode:" << ret;
+ continue;
+ }
+ OHPrinter p;
+ p.id = idString;
+ p.name = QString::fromUtf8(info->printerName);
+ p.isDefault = info->isDefaultPrinter;
+ OH_Print_ReleasePrinterInfo(info);
+ m_printers << p;
+ }
+ OH_Print_ReleasePrinterList(&printerIdList);
+}
+
+bool QOpenHarmonyPrinterSupport::contains(const QString &id)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).id == id)
+ return true;
+ }
+ return false;
+}
+
+QStringList QOpenHarmonyPrinterSupport::availablePrintDeviceIds() const
+{
+ QStringList result;
+ for (int i = 0; i < m_printers.count(); ++i) {
+ result << m_printers.at(i).name;
+ }
+ return result;
+}
+
+QString QOpenHarmonyPrinterSupport::defaultPrintDeviceId() const
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).isDefault)
+ return m_printers.at(i).name;
+ }
+ return QString();
+}
+
+void QOpenHarmonyPrinterSupport::add(const OHPrinter &p)
+{
+ m_printers << p;
+}
+
+void QOpenHarmonyPrinterSupport::remove(const QString &id)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).id == id) {
+ m_printers.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QOpenHarmonyPrinterSupport::deletePrinter(const OHPrinter &p)
+{
+ auto erased = std::remove_if(m_printers.begin(), m_printers.end(), [p](const OHPrinter& printer){
+ return printer.id == p.id;
+ });
+
+ m_printers.erase(erased, m_printers.end());
+}
+
+QString QOpenHarmonyPrinterSupport::defaultPrinterId() const
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).isDefault)
+ return m_printers.at(i).id;
+ }
+ return QString();
+}
+
+QString QOpenHarmonyPrinterSupport::idFromName(const QString &name)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).name == name)
+ return m_printers.at(i).id;
+ }
+ return QString();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYSPRINTERSUPPORT_H
+#define QOPENHARMONYSPRINTERSUPPORT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qpa/qplatformprintersupport.h>
+
+#include <QtCore/qstringlist.h>
+#include <BasicServicesKit/ohprint.h>
+
+QT_BEGIN_NAMESPACE
+
+struct OHPrinter
+{
+ QString name;
+ QString id;
+ bool isDefault;
+};
+
+class QOpenHarmonyPrinterSupport : public QPlatformPrinterSupport
+{
+public:
+ QOpenHarmonyPrinterSupport();
+ ~QOpenHarmonyPrinterSupport();
+
+ QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override;
+ QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) override;
+
+ QPrintDevice createPrintDevice(const QString &id) override;
+ QStringList availablePrintDeviceIds() const override;
+ QString defaultPrintDeviceId() const override;
+ void add(const OHPrinter &p);
+ void remove(const QString &id);
+ void deletePrinter(const OHPrinter &p);
+private:
+ QString defaultPrinterId() const;
+ QString idFromName(const QString &name);
+ void detectOHPrinter();
+ bool contains(const QString &id);
+ QString cupsOption(int i, const QString &key) const;
+
+ static void Print_PrinterChange(Print_PrinterEvent event, const Print_PrinterInfo *printerInfo);
+ static void Print_PrinterDiscovery(Print_DiscoveryEvent event, const Print_PrinterInfo *printerInfo);
+ QList<OHPrinter> m_printers;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYSPRINTERSUPPORT_H
@@ -77,6 +77,20 @@ qt_internal_extend_target(PrintSupport CONDITION WIN32
platform/windows/qwindowsprintersupport.cpp
)
+qt_internal_extend_target(PrintSupport CONDITION OHOS
+ SOURCES
+ platform/openharmony/main.cpp
+ platform/openharmony/qopenharmonyprintdevice.cpp platform/openharmony/qopenharmonyprintdevice.h
+ platform/openharmony/qopenharmonyprintengine.cpp platform/openharmony/qopenharmonyprintengine_p.h
+ platform/openharmony/qopenharmonyprintersupport.cpp platform/openharmony/qopenharmonyprintersupport_p.h
+ LIBRARIES
+ ohprint
+ ohfileuri
+ ace_ndk.z
+ rawfile.z
+ ace_napi.z
+)
+
qt_internal_extend_target(PrintSupport CONDITION QT_FEATURE_printpreviewwidget
SOURCES
kernel/qpaintengine_preview.cpp kernel/qpaintengine_preview_p.h
@@ -87,6 +87,10 @@ private:
friend class QCupsPrintEngine;
friend class QCupsPrintEnginePrivate;
+#ifdef Q_OS_OPENHARMONY
+ friend class QOpenHarmonyPrintEngine;
+ friend class QOpenHarmonyPrintEnginePrivate;
+#endif
QString printerName;
QString printProgram;
new file mode 100644
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include <qpa/qplatformprintplugin.h>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+
+class QOhPrinterSupportPlugin : public QPlatformPrinterSupportPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformPrinterSupportFactoryInterface_iid FILE "openharmonyprint.json")
+
+public:
+ QStringList keys() const;
+ QPlatformPrinterSupport *create(const QString &) override;
+};
+
+QStringList QOhPrinterSupportPlugin::keys() const
+{
+ return QStringList(QStringLiteral("openharmonyprintersupport"));
+}
+
+QPlatformPrinterSupport *QOhPrinterSupportPlugin::create(const QString &key)
+{
+ if (key.compare(key, QLatin1String("openharmonyprintersupport"), Qt::CaseInsensitive) == 0)
+ return new QOpenHarmonyPrinterSupport;
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
new file mode 100644
@@ -0,0 +1,22 @@
+TARGET = openharmonyprintersupport
+MODULE = openharmonyprintersupport
+
+QT += core-private gui-private printsupport printsupport-private
+LIBS += -lohprint -lace_napi.z -lohfileuri -lace_ndk.z -lrawfile.z
+
+INCLUDEPATH += ../../../printsupport/kernel
+
+SOURCES += main.cpp \
+ qopenharmonyprintdevice.cpp \
+ qopenharmonyprintersupport.cpp \
+ qopenharmonyprintengine.cpp
+
+HEADERS += qopenharmonyprintersupport_p.h \
+ qopenharmonyprintdevice.h \
+ qopenharmonyprintengine_p.h
+
+OTHER_FILES += openharmonyprint.json
+
+PLUGIN_TYPE = printsupport
+PLUGIN_CLASS_NAME = QOpenHarmonyPrinterSupportPlugin
+load(qt_plugin)
new file mode 100644
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "openharmonyprintersupport" ]
+}
new file mode 100644
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonyprintdevice.h"
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include <QDebug>
+
+#if QT_CONFIG(mimetype)
+#include <QtCore/QMimeDatabase>
+#endif
+
+QT_BEGIN_NAMESPACE
+#define PDPK_OHOSPrinterId QPrintDevice::PrintDevicePropertyKey(QPrintDevice::PDPK_CustomBase + 100)
+QOpenHarmonyPrintDevice::QOpenHarmonyPrintDevice(const QString &id)
+ : QPlatformPrintDevice(id),
+ m_printerInfo(new Print_PrinterInfo),
+ m_ppd(0)
+{
+ if (!id.isEmpty()) {
+ m_PrinterId = id.toUtf8();
+ // Get the print instance and PPD file
+ //鸿蒙native接口中没有ppd相关接口。
+ int ret = OH_Print_QueryPrinterInfo(m_PrinterId.constData(), &m_printerInfo);
+ if(PRINT_ERROR_NONE == ret){
+// m_id = m_printerInfo->printerId;
+ m_id = m_printerInfo->printerName;
+ m_name = m_printerInfo->printerName;
+ m_location = m_printerInfo->location;
+ m_makeAndModel = m_printerInfo->makeAndModel;
+ //鸿蒙native接口中没有下面几个信息,暂定为false。
+ m_isRemote = false;
+ m_supportsMultipleCopies = false;
+ m_supportsCollateCopies = false;
+ m_supportsCustomPageSizes = false;
+ }
+ else{
+ qWarning() << "QOpenHarmonyPrintDevice:query printer info fail,errCode:" << ret;
+ m_printerInfo = nullptr;
+ }
+ }else{
+ m_printerInfo = nullptr;
+ qWarning() << "QOpenHarmonyPrintDevice:device id is empty";
+ }
+}
+
+QOpenHarmonyPrintDevice::~QOpenHarmonyPrintDevice()
+{
+ if (m_ppd)
+ //鸿蒙native接口中没有ppd相关接口。
+ //ppdClose(m_ppd);
+ if (m_printerInfo)
+ OH_Print_ReleasePrinterInfo(m_printerInfo);
+ m_printerInfo = 0;
+ m_ppd = 0;
+}
+
+bool QOpenHarmonyPrintDevice::isValid() const
+{
+ return m_printerInfo;
+}
+
+bool QOpenHarmonyPrintDevice::isDefault() const
+{
+ return m_printerInfo->isDefaultPrinter;
+}
+
+QPrint::DeviceState QOpenHarmonyPrintDevice::state() const
+{
+ Print_PrinterState state = m_printerInfo ? m_printerInfo->printerState : PRINTER_UNAVAILABLE;
+ if (PRINTER_IDLE == state)
+ return QPrint::Idle;
+ else if (PRINTER_BUSY == state)
+ /* TODO 待确定PRINTER_BUSY对应Active还是Aborted */
+ return QPrint::Active;
+ else if (PRINTER_UNAVAILABLE == state)
+ return QPrint::Error;
+ else
+ return QPrint::Error;
+}
+
+extern qreal qt_pointMultiplier(QPageLayout::Unit unit);
+void QOpenHarmonyPrintDevice::loadPageSizes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_pageSizes.clear();
+ m_printableMargins.clear();
+
+ Print_PrinterCapability printerCapability = m_printerInfo->capability;
+ Print_Margin defaultMargin = m_printerInfo->defaultValue.defaultMargin;
+ uint32_t pageSizesCount = printerCapability.supportedPageSizesCount;
+ Print_PageSize *pageSize = printerCapability.supportedPageSizes;
+ static auto conver = [](uint32_t value)->int {
+ const double factor = 25.4 * qt_pointMultiplier(QPageLayout::Millimeter) / 1000.0;
+ return qRound(value * factor);
+ };
+ for(uint32_t i = 0; i < pageSizesCount; ++i){
+ QString key = pageSize[i].id;
+ QSize size = QSize(conver(pageSize[i].width), conver(pageSize[i].height));
+ QString name = QString::fromLocal8Bit(pageSize[i].name);
+ if (!size.isEmpty()) {
+ QPageSize ps = createPageSize(key, size, name);
+ qWarning() << "create page size:" << ps << "original size:" << QSize(pageSize[i].width, pageSize[i].height);
+ if (ps.isValid()) {
+ m_pageSizes.append(ps);
+ m_printableMargins.insert(key, QMarginsF(defaultMargin.leftMargin, defaultMargin.topMargin,
+ defaultMargin.rightMargin, defaultMargin.bottomMargin));
+ }
+ }
+ }
+
+ m_havePageSizes = true;
+}
+
+QPageSize QOpenHarmonyPrintDevice::defaultPageSize() const
+{
+ if(!m_printerInfo)
+ return QPageSize();
+
+ Print_DefaultValue defaultVal = m_printerInfo->defaultValue;
+ QString key = QString::fromLocal8Bit(defaultVal.defaultPageSizeId);
+ auto it = std::find_if(m_pageSizes.constBegin(), m_pageSizes.constEnd(),[&key](const QPageSize &page){
+ return key == page.key();
+ });
+
+ return it == m_pageSizes.constEnd() ? QPageSize() : *it;
+}
+
+QMarginsF QOpenHarmonyPrintDevice::printableMargins(const QPageSize &pageSize,
+ QPageLayout::Orientation orientation,
+ int resolution) const
+{
+ Q_UNUSED(orientation)
+ Q_UNUSED(resolution)
+ if (!m_havePageSizes)
+ loadPageSizes();
+ // TODO Orientation?
+ if (m_printableMargins.contains(pageSize.key()))
+ return m_printableMargins.value(pageSize.key());
+ return m_customMargins;
+}
+
+void QOpenHarmonyPrintDevice::loadResolutions() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_resolutions.clear();
+
+ Print_Resolution *resolution = m_printerInfo->capability.supportedResolutions;
+ uint resolutionCount = m_printerInfo->capability.supportedResolutionsCount;
+
+ //Try load printer supportedResolutions
+ if(resolutionCount > 0){
+ for (int i = 0; i < resolutionCount; ++i) {
+ /* FIXME 暂定水平和垂直分辨率相同 */
+ if((resolution[i].horizontalDpi > 0) &&
+ (!m_resolutions.contains(resolution[i].horizontalDpi))){
+ m_resolutions.append(resolution[i].horizontalDpi);
+ }
+ }
+ }
+
+ // If no result, try just the default
+ if (m_resolutions.size() == 0) {
+ Print_Resolution defaultResolution = m_printerInfo->defaultValue.defaultResolution;
+ if((defaultResolution.horizontalDpi > 0) &&
+ (!m_resolutions.contains(defaultResolution.horizontalDpi))){
+ m_resolutions.append(defaultResolution.horizontalDpi);
+ }
+ }
+
+ m_haveResolutions = true;
+}
+
+int QOpenHarmonyPrintDevice::defaultResolution() const
+{
+ if(!m_printerInfo)
+ return 72;
+
+ Print_Resolution defaultResolution = m_printerInfo->defaultValue.defaultResolution;
+ int res = defaultResolution.horizontalDpi;
+ if(res > 0){
+ return res;
+ }
+ // Otherwise return a sensible default.
+ // TODO What is sensible? 150? 300?
+ return 72;
+}
+
+//todo:鸿蒙native接口中没有InputSlots信息
+void QOpenHarmonyPrintDevice::loadInputSlots() const
+{
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // TODO Deal with concatenated names like Tray1Manual or Tray1_Man,
+ // // will currently show as CustomInputSlot
+ // // TODO Deal with separate ManualFeed key
+ // // Try load standard PPD options first
+ // m_inputSlots.clear();
+ // if (m_ppd) {
+ // ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot");
+ // if (inputSlots) {
+ // m_inputSlots.reserve(inputSlots->num_choices);
+ // for (int i = 0; i < inputSlots->num_choices; ++i)
+ // m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i]));
+ // }
+ // // If no result, try just the default
+ // if (m_inputSlots.size() == 0) {
+ // inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot");
+ // if (inputSlots)
+ // m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0]));
+ // }
+ // }
+ // // If still no result, just use Auto
+ // if (m_inputSlots.size() == 0)
+ // m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot());
+ // m_haveInputSlots = true;
+}
+
+QPrint::InputSlot QOpenHarmonyPrintDevice::defaultInputSlot() const
+{
+ //todo:鸿蒙native接口中没有InputSlots信息
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // Try load standard PPD option first
+ // if (m_ppd) {
+ // ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot");
+ // if (inputSlot)
+ // return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]);
+ // // If no result, then try a marked option
+ // ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot");
+ // if (defaultChoice)
+ // return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice);
+ // }
+ // Otherwise return Auto
+ return QPlatformPrintDevice::defaultInputSlot();
+}
+//todo:鸿蒙native接口中没有OutputBins信息
+void QOpenHarmonyPrintDevice::loadOutputBins() const
+{
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // m_outputBins.clear();
+ // if (m_ppd) {
+ // ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin");
+ // if (outputBins) {
+ // m_outputBins.reserve(outputBins->num_choices);
+ // for (int i = 0; i < outputBins->num_choices; ++i)
+ // m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i]));
+ // }
+ // // If no result, try just the default
+ // if (m_outputBins.size() == 0) {
+ // outputBins = ppdFindOption(m_ppd, "DefaultOutputBin");
+ // if (outputBins)
+ // m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0]));
+ // }
+ // }
+ // // If still no result, just use Auto
+ // if (m_outputBins.size() == 0)
+ // m_outputBins.append(QPlatformPrintDevice::defaultOutputBin());
+ // m_haveOutputBins = true;
+}
+
+QPrint::OutputBin QOpenHarmonyPrintDevice::defaultOutputBin() const
+{
+ //todo:鸿蒙native接口中没有OutputBins信息
+ // // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
+ // // Try load standard PPD option first
+ // if (m_ppd) {
+ // ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin");
+ // if (outputBin)
+ // return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]);
+ // // If no result, then try a marked option
+ // ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin");
+ // if (defaultChoice)
+ // return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice);
+ // }
+ // Otherwise return AutoBin
+ return QPlatformPrintDevice::defaultOutputBin();
+}
+
+void QOpenHarmonyPrintDevice::loadDuplexModes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ // Try load standard PPD options first
+ m_duplexModes.clear();
+ Print_DuplexMode *duplexModes = m_printerInfo->capability.supportedDuplexModes;
+ uint duplexModesCount = m_printerInfo->capability.supportedDuplexModesCount;
+ QPrint::DuplexMode qtDuplexMode;
+
+ if(duplexModes){
+ m_duplexModes.reserve(duplexModesCount);
+ for (int i = 0; i < duplexModesCount; ++i) {
+ switch(*(duplexModes+i)){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ if(!m_duplexModes.contains(qtDuplexMode)){
+ m_duplexModes.append(qtDuplexMode);
+ }
+ }
+ }
+
+ if (m_duplexModes.size() == 0) {
+ Print_DuplexMode defaultDuplexMode = m_printerInfo->defaultValue.defaultDuplexMode;
+ switch(defaultDuplexMode){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ if(!m_duplexModes.contains(qtDuplexMode)){
+ m_duplexModes.append(qtDuplexMode);
+ }
+ }
+
+ // If still no result, or not added in PPD, then add None
+ if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone))
+ m_duplexModes.append(QPrint::DuplexNone);
+ // If have both modes, then can support DuplexAuto
+ if (m_duplexModes.contains(QPrint::DuplexLongSide) && m_duplexModes.contains(QPrint::DuplexShortSide))
+ m_duplexModes.append(QPrint::DuplexAuto);
+
+ m_haveDuplexModes = true;
+}
+
+QPrint::DuplexMode QOpenHarmonyPrintDevice::defaultDuplexMode() const
+{
+ QPrint::DuplexMode qtDuplexMode;
+ Print_DuplexMode defaultDuplexMode = m_printerInfo ? m_printerInfo->defaultValue.defaultDuplexMode : DUPLEX_MODE_ONE_SIDED;
+ switch(defaultDuplexMode){
+ case DUPLEX_MODE_ONE_SIDED:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_LONG_EDGE:
+ qtDuplexMode = QPrint::DuplexLongSide;
+ break;
+ case DUPLEX_MODE_TWO_SIDED_SHORT_EDGE:
+ qtDuplexMode = QPrint::DuplexShortSide;
+ break;
+ default:
+ qtDuplexMode = QPrint::DuplexNone;
+ break;
+ }
+ return qtDuplexMode;
+}
+
+void QOpenHarmonyPrintDevice::loadColorModes() const
+{
+ if(!m_printerInfo)
+ return;
+
+ m_colorModes.clear();
+
+ Print_ColorMode *colorModes = m_printerInfo->capability.supportedColorModes;
+ uint colorModesCount = m_printerInfo->capability.supportedColorModesCount;
+ QPrint::ColorMode qtColorMode;
+
+ for (int i = 0; i < colorModesCount; ++i) {
+ switch(*(colorModes+i)){
+ case COLOR_MODE_MONOCHROME:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ case COLOR_MODE_COLOR:
+ qtColorMode = QPrint::Color;
+ break;
+ case COLOR_MODE_AUTO:
+ qtColorMode = QPrint::Color;
+ break;
+ default:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ }
+ if(!m_colorModes.contains(qtColorMode)){
+ m_colorModes.append(qtColorMode);
+ }
+ }
+
+ m_haveColorModes = true;
+}
+
+QPrint::ColorMode QOpenHarmonyPrintDevice::defaultColorMode() const
+{
+ // Not a proper option, usually only know if supports color or not, but some
+ // users known to abuse ColorModel to always force GrayScale.
+ if (supportedColorModes().contains(QPrint::Color)) {
+ QPrint::ColorMode qtColorMode = QPrint::GrayScale;
+
+ if(m_printerInfo && m_printerInfo->printerId != nullptr){
+ Print_ColorMode defaultColorMode = m_printerInfo->defaultValue.defaultColorMode;
+ switch(defaultColorMode){
+ case COLOR_MODE_MONOCHROME:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ case COLOR_MODE_COLOR:
+ qtColorMode = QPrint::Color;
+ break;
+ case COLOR_MODE_AUTO:
+ qtColorMode = QPrint::Color;
+ break;
+ default:
+ qtColorMode = QPrint::GrayScale;
+ break;
+ }
+ }
+
+ if(QPrint::Color == qtColorMode)
+ {
+ return qtColorMode;
+ }
+ }
+
+ return QPrint::GrayScale;
+}
+
+QVariant QOpenHarmonyPrintDevice::property(QPrintDevice::PrintDevicePropertyKey key) const
+{
+ //todo:鸿蒙native没有这些接口
+ // if (key == PDPK_PpdFile)
+ // return QVariant::fromValue<ppd_file_t *>(m_ppd);
+ // else if (key == PDPK_CupsJobPriority)
+ // return printerOption(QStringLiteral("job-priority"));
+ // else if (key == PDPK_CupsJobSheets)
+ // return printerOption(QStringLiteral("job-sheets"));
+ // else if (key == PDPK_CupsJobBilling)
+ // return printerOption(QStringLiteral("job-billing"));
+ // else if (key == PDPK_CupsJobHoldUntil)
+ // return printerOption(QStringLiteral("job-hold-until"));
+ if (key == PDPK_OHOSPrinterId) {
+ return m_PrinterId;
+ }
+
+ return QPlatformPrintDevice::property(key);
+}
+
+bool QOpenHarmonyPrintDevice::setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value)
+{
+ //todo:鸿蒙native没有该接口
+ // if (key == PDPK_PpdOption) {
+ // const QStringList values = value.toStringList();
+ // if (values.count() == 2) {
+ // ppdMarkOption(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ // return true;
+ // }
+ // }
+
+ return QPlatformPrintDevice::setProperty(key, value);
+}
+
+bool QOpenHarmonyPrintDevice::isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant ¶ms) const
+{
+ //todo:鸿蒙native没有该接口
+ // if (key == PDPK_PpdChoiceIsInstallableConflict) {
+ // const QStringList values = params.toStringList();
+ // if (values.count() == 2)
+ // return ppdInstallableConflict(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ // }
+
+ return QPlatformPrintDevice::isFeatureAvailable(key, params);
+}
+
+#if QT_CONFIG(mimetype)
+void QOpenHarmonyPrintDevice::loadMimeTypes() const
+{
+ QMimeDatabase db;
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/pdf")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/postscript")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/gif")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/png")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/jpeg")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/tiff")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/html")));
+ m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/plain")));
+ m_haveMimeTypes = true;
+}
+#endif
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOpenHarmonyPrintDevice_H
+#define QOpenHarmonyPrintDevice_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of internal files. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtPrintSupport/qprintengine.h"
+#include <qpa/qplatformprintdevice.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmargins.h>
+#include <BasicServicesKit/ohprint.h>
+
+QT_BEGIN_NAMESPACE
+
+struct oh_ppd_file_t; //todo:仅占位,鸿蒙native接口没有该结构体。
+
+class QOpenHarmonyPrintDevice : public QPlatformPrintDevice
+{
+public:
+ explicit QOpenHarmonyPrintDevice(const QString &id);
+ virtual ~QOpenHarmonyPrintDevice();
+
+ bool isValid() const override;
+ bool isDefault() const override;
+
+ QPrint::DeviceState state() const override;
+
+ QPageSize defaultPageSize() const override;
+
+ QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation,
+ int resolution) const override;
+
+ int defaultResolution() const override;
+
+ QPrint::InputSlot defaultInputSlot() const override;
+
+ QPrint::OutputBin defaultOutputBin() const override;
+
+ QPrint::DuplexMode defaultDuplexMode() const override;
+
+ QPrint::ColorMode defaultColorMode() const override;
+
+ QVariant property(QPrintDevice::PrintDevicePropertyKey key) const override;
+ bool setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value) override;
+ bool isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant ¶ms) const override;
+
+protected:
+ void loadPageSizes() const override;
+ void loadResolutions() const override;
+ void loadInputSlots() const override;
+ void loadOutputBins() const override;
+ void loadDuplexModes() const override;
+ void loadColorModes() const override;
+#if QT_CONFIG(mimetype)
+ void loadMimeTypes() const override;
+#endif
+
+private:
+ Print_PrinterInfo *m_printerInfo;
+ oh_ppd_file_t *m_ppd;
+ QByteArray m_PrinterId;
+ QMarginsF m_customMargins;
+ mutable QHash<QString, QMarginsF> m_printableMargins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOpenHarmonyPrintDevice_H
new file mode 100644
@@ -0,0 +1,515 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <qfile.h>
+#include <qdebug.h>
+#include <qbuffer.h>
+#include <QFileInfo>
+#include <qiodevice.h>
+#include <QPrintDialog>
+#include <QStandardPaths>
+#include <QtGui/qpagelayout.h>
+
+#include <qpa/qplatformprintplugin.h>
+#include <BasicServicesKit/ohprint.h>
+#include <qpa/qplatformprintersupport.h>
+
+#include "private/qcore_unix_p.h" // overrides QT_OPEN
+#include "qopenharmonyprintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+#define PDPK_OHOSPrinterId QPrintDevice::PrintDevicePropertyKey(QPrintDevice::PDPK_CustomBase + 100)
+extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits);
+
+QOpenHarmonyPrintEngine::QOpenHarmonyPrintEngine(QPrinter::PrinterMode m, const QString &deviceId)
+ : QPdfPrintEngine(*new QOpenHarmonyPrintEnginePrivate(m))
+{
+ Q_D(QOpenHarmonyPrintEngine);
+ d->changePrinter(deviceId);
+ state = QPrinter::Idle;
+}
+
+QOpenHarmonyPrintEngine::~QOpenHarmonyPrintEngine()
+{
+}
+
+void QOpenHarmonyPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QOpenHarmonyPrintEngine);
+
+ switch (int(key)) {
+ case PPK_PageSize:
+ d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt())));
+ break;
+ case PPK_WindowsPageSize:
+ d->setPageSize(QPageSize(QPageSize::id(value.toInt())));
+ break;
+ case PPK_CustomPaperSize:
+ d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point));
+ break;
+ case PPK_PaperName:
+ // Get the named page size from the printer if supported
+ d->setPageSize(d->m_printDevice.supportedPageSize(value.toString()));
+ break;
+ case PPK_Duplex: {
+ QPrint::DuplexMode mode = QPrint::DuplexMode(value.toInt());
+ if (d->m_printDevice.supportedDuplexModes().contains(mode)) {
+ d->duplex = mode;
+ d->duplexRequestedExplicitly = true;
+ }
+ break;
+ }
+ case PPK_Orientation: {
+ d->m_pageLayout.setOrientation(QPageLayout::Orientation(value.toInt()));
+ qWarning() << "set ppk orientation:" << d->m_pageLayout;
+ }
+ break;
+ case PPK_PrinterName:
+ d->changePrinter(value.toString());
+ break;
+ case PPK_OhPrintOptions:
+ d->ohOptions = value.toStringList();
+ break;
+ case PPK_QPageSize:
+ d->setPageSize(qvariant_cast<QPageSize>(value));
+ break;
+ case PPK_QPageLayout: {
+ QPageLayout pageLayout = qvariant_cast<QPageLayout>(value);
+ d->m_pageLayout = pageLayout;
+ qWarning() << "set ppk page layout:" << d->m_pageLayout;
+#if 0
+ if (pageLayout.isValid() && (d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)
+ || d->m_printDevice.supportsCustomPageSizes()
+ || d->m_printDevice.supportedPageSizes().isEmpty())) {
+ // supportedPageSizes().isEmpty() because QPageSetupWidget::initPageSizes says
+ // "If no available printer page sizes, populate with all page sizes"
+ d->m_pageLayout = pageLayout;
+ d->setPageSize(pageLayout.pageSize());
+ }
+#endif
+ break;
+ }
+ default:
+ QPdfPrintEngine::setProperty(key, value);
+ break;
+ }
+}
+
+QVariant QOpenHarmonyPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QOpenHarmonyPrintEngine);
+
+ QVariant ret;
+ switch (int(key)) {
+ case PPK_SupportsMultipleCopies:
+ //不确定是否支持该属性,暂时认为不支持。
+ ret = false;
+ break;
+ case PPK_NumberOfCopies:
+ ret = 1;
+ break;
+ case PPK_OhPrintOptions:
+ ret = d->ohOptions;
+ break;
+ case PPK_Duplex:
+ ret = d->duplex;
+ break;
+ default:
+ ret = QPdfPrintEngine::property(key);
+ break;
+ }
+ return ret;
+}
+
+
+QOpenHarmonyPrintEnginePrivate::QOpenHarmonyPrintEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfPrintEnginePrivate(m)
+ , duplex(QPrint::DuplexNone)
+ , printMode(m)
+{
+ outputFileName.clear();
+ ownsDevice = true; /* FIXME 是否让PDF基类控制文件内容落盘 */
+}
+
+QOpenHarmonyPrintEnginePrivate::~QOpenHarmonyPrintEnginePrivate()
+{
+}
+
+bool QOpenHarmonyPrintEnginePrivate::openPrintDevice()
+{
+ if (this->outDevice)
+ return false;
+
+ if (!outputFileName.isEmpty()) {
+ QFile *file = new QFile(outputFileName);
+ if (!file->open(QFile::WriteOnly|QFile::Truncate)) {
+ delete file;
+ return false;
+ }
+ this->outDevice = file;
+ } else {
+ QUuid uuid = QUuid::createUuid();
+ const QString &tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ ohTempFile = QString("%1/%2.pdf").arg(tempDir, uuid.toString(QUuid::Id128));
+ fd = open(ohTempFile.toLocal8Bit().constData(), O_CREAT | O_SYNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP);
+ QFile *f = new QFile();
+ // if (!f->open(QIODevice::WriteOnly)) {
+ // f->close();
+ // qWarning() << "create print temp file failed:" << f->errorString();
+ // return false;
+ // }
+ this->outDevice = f;
+ static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
+ //this->fd = f->handle();
+ qWarning() << "open tmp file:" << ohTempFile << "fd:" << fd << outDevice;
+ }
+
+ return true;
+}
+
+void QOpenHarmonyPrintEnginePrivate::closePrintDevice()
+{
+ Q_Q(QOpenHarmonyPrintEngine);
+ if (!ohTempFile.isEmpty()) {
+ QString tempFile = ohTempFile;
+ ohTempFile.clear();
+
+ // Should never have got here without a printer, but check anyway
+ if (printerName.isEmpty()) {
+ qWarning("Could not determine printer to print to");
+ QFile::remove(tempFile);
+ return;
+ }
+
+#if 0
+ Print_ErrorCode errCode = OH_Print_ConnectPrinter(printerName.toLocal8Bit().constData());
+ if (Print_ErrorCode::PRINT_ERROR_NONE != errCode)
+ {
+ qWarning() << "OnPrint OH_Print_ConnectPrinter error:" << errCode;
+ OH_Print_StopPrinterDiscovery();
+ OH_Print_UnregisterPrinterChangeListener();
+ OH_Print_Release();
+ return;
+ }
+#endif
+ // Set up print options.
+ QList<QPair<QByteArray, QByteArray> > options;
+ QVector<Print_Property> ohPrintOptStruct;
+
+
+ options.append(QPair<QByteArray, QByteArray>("media", m_pageLayout.pageSize().key().toLocal8Bit()));
+
+ if (copies > 1)
+ options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
+
+ if (copies > 1 && collate)
+ options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
+
+ switch (duplex) {
+ case QPrint::DuplexNone:
+ options.append(QPair<QByteArray, QByteArray>("sides", "one-sided"));
+ break;
+ case QPrint::DuplexAuto:
+ if (m_pageLayout.orientation() == QPageLayout::Portrait)
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ else
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ case QPrint::DuplexLongSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ break;
+ case QPrint::DuplexShortSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ }
+
+ if (m_pageLayout.orientation() == QPageLayout::Landscape)
+ options.append(QPair<QByteArray, QByteArray>("landscape", ""));
+
+ QStringList::const_iterator it = ohOptions.constBegin();
+ while (it != ohOptions.constEnd()) {
+ options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
+ it += 2;
+ }
+
+ const int numOptions = options.size();
+ ohPrintOptStruct.reserve(numOptions);
+ for (int c = 0; c < numOptions; ++c) {
+ Print_Property prop;
+ prop.key = options[c].first.data();
+ prop.value = options[c].second.data();
+ ohPrintOptStruct.append(prop);
+ }
+#if 0
+ int thisFd = -1;
+ QFile file(tempFile);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << "get file:" << tempFile << "failed:" << file.errorString();
+ file.close();
+ return;
+ }
+ thisFd = file.handle();
+ file.close();
+#endif
+
+ Print_PrintJob printJob;
+#if 0
+ /* 公共目录下文件无法打印,拷贝到临时目录下 */
+ //QFile sourceFile("/storage/Users/currentUser/DowLoad/123.pdf");
+ QFile sourceFile(tempFile);
+ QTemporaryDir temporaryDir;
+ QString temporaryDirPath = temporaryDir.path();
+ QString destinationFilePath = QDir(temporaryDirPath).filePath(sourceFile.fileName());
+ qDebug() << "destinationFilePath:" << destinationFilePath;
+ if (!sourceFile.copy(destinationFilePath)) {
+ qDebug() << "copy failed";
+ }
+#endif
+
+ //QFile::copy("/storage/Users/currentUser/Desktop/wanghao_test222.pdf", "/data/storage/el2/base/temp/test.pdf");
+ //fd = open("/data/storage/el2/base/temp/test.pdf", O_RDONLY);
+ /* FIXME 何时close文件? */
+ fd = open(tempFile.toLocal8Bit().constData(), O_RDONLY);
+ // QFile ff(tempFile);
+ // ff.open(QIODevice::ReadOnly);
+ // fd = ff.handle();
+
+ // struct stat st;
+ // fstat(fd, &st);
+
+ // qWarning() << st.st_size << fd
+ // << tempFile << m_pageLayout;
+
+ QString jobName = title.isEmpty() ? QUuid::createUuid().toString(QUuid::Id128) : title;
+ QByteArray jobNameArray = jobName.toUtf8();
+ std::unique_ptr<char[]> cjobName(new char[jobNameArray.length() + 1]);
+ memset(cjobName.get(), 0, jobNameArray.length() + 1);
+ memcpy(cjobName.get(), jobNameArray.constData(), jobNameArray.length());
+ printJob.jobName = cjobName.get();
+ printJob.fdList = new uint32_t[1]{0};
+ printJob.fdList[0] = fd;
+ printJob.fdListCount = 1;
+
+ /* FIXME OH_Print_StartPrintJob接口负责删除? 不这样会有内存访问问题 */
+ char *id = new char[printerName.length() + 1]{0};
+ memcpy(id, printerName.toLocal8Bit().data(), printerName.length());
+ printJob.printerId = id;
+ printJob.copyNumber = copies;
+ // printJob.paperSource = q->property(PPK_PaperSources);
+ printJob.paperSource = "auto";
+ printJob.mediaType = "stationery";
+ QString key = m_pageLayout.pageSize().key();
+ std::unique_ptr<char[]> ckey(new char[key.length() + 1]);
+ memset(ckey.get(), 0, key.length() + 1);
+ memcpy(ckey.get(), key.toLocal8Bit().data(), key.length());
+ printJob.pageSizeId = ckey.get();
+ //printJob.pageSizeId = "ISO_A4";
+ //Print_ColorMode
+ Print_ColorMode ohColorMode;
+ QPrinter::ColorMode qtColorMode = (QPrinter::ColorMode)q->property(QPrintEngine::PPK_ColorMode).toInt();
+ switch(qtColorMode){
+ case QPrinter::GrayScale:
+ ohColorMode = COLOR_MODE_MONOCHROME;
+ break;
+ case QPrinter::Color:
+ ohColorMode = COLOR_MODE_COLOR;
+ break;
+ default:
+ ohColorMode = COLOR_MODE_MONOCHROME;
+ break;
+ }
+ printJob.colorMode = ohColorMode;
+
+
+ //Print_DuplexMode
+ Print_DuplexMode ohDuplexMode;
+ QPrint::DuplexMode qtDuplexMode = (QPrint::DuplexMode)q->property(QPrintEngine::PPK_Duplex).toInt();
+ switch(qtDuplexMode){
+ case QPrinter::DuplexNone:
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ case QPrinter::DuplexAuto:
+ //自动模式暂时设置为One sided。
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ case QPrinter::DuplexLongSide:
+ ohDuplexMode = DUPLEX_MODE_TWO_SIDED_LONG_EDGE;
+ break;
+ case QPrinter::DuplexShortSide:
+ ohDuplexMode = DUPLEX_MODE_TWO_SIDED_SHORT_EDGE;
+ break;
+ default:
+ ohDuplexMode = DUPLEX_MODE_ONE_SIDED;
+ break;
+ }
+ printJob.duplexMode = ohDuplexMode;
+
+ //Print_Resolution
+ int resolution = 600/*q->property(QPrintEngine::PPK_Resolution).toInt()*/;
+ printJob.resolution.horizontalDpi = resolution;
+ printJob.resolution.verticalDpi = resolution;
+
+ //Print_Margin
+ Print_Margin ohPrintMargin;
+ QPair<QMarginsF, QPageLayout::Unit> pair = qvariant_cast<QPair<QMarginsF, QPageLayout::Unit> >(q->property(QPrintEngine::PPK_QPageMargins));
+ ohPrintMargin.leftMargin = static_cast<uint>(pair.first.left());
+ ohPrintMargin.topMargin = static_cast<uint>(pair.first.top());
+ ohPrintMargin.rightMargin = static_cast<uint>(pair.first.right());
+ ohPrintMargin.bottomMargin = static_cast<uint>(pair.first.bottom());
+ printJob.printMargin = ohPrintMargin;
+ /* 默认无border */
+ printJob.borderless = false;
+
+ //Print_OrientationMode
+ Print_OrientationMode ohOrientationMode;
+ // int ohOrientationMode;
+ QPageLayout::Orientation qtOrientation = m_pageLayout.orientation();
+ switch(qtOrientation){
+ case QPageLayout::Portrait:
+ ohOrientationMode = ORIENTATION_MODE_PORTRAIT;
+ break;
+ case QPageLayout::Landscape:
+ ohOrientationMode = ORIENTATION_MODE_LANDSCAPE;
+ break;
+ default:
+ ohOrientationMode = ORIENTATION_MODE_PORTRAIT;
+ break;
+ }
+ printJob.orientationMode = ohOrientationMode;
+
+ //Print_Quality
+ Print_Quality ohQuality;
+ switch(printMode){
+ case QPrinter::ScreenResolution:
+ ohQuality = PRINT_QUALITY_DRAFT;
+ break;
+ case QPrinter::PrinterResolution:
+ ohQuality = PRINT_QUALITY_NORMAL;
+ break;
+ case QPrinter::HighResolution:
+ ohQuality = PRINT_QUALITY_HIGH;
+ break;
+ default:
+ ohQuality = PRINT_QUALITY_HIGH;
+ break;
+ }
+ printJob.printQuality = ohQuality;
+
+
+ //Print_DocumentFormat
+ Print_DocumentFormat ohDocumentFormat;
+ QFileInfo fi(outputFileName);
+ if (!fi.suffix().compare(QLatin1String("pdf"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_PDF;
+ else if (!fi.suffix().compare(QLatin1String("jpeg"), Qt::CaseInsensitive) ||
+ !fi.suffix().compare(QLatin1String("jpg"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_JPEG;
+ else if (!fi.suffix().compare(QLatin1String("ps"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_POSTSCRIPT;
+ else if (!fi.suffix().compare(QLatin1String("txt"), Qt::CaseInsensitive))
+ ohDocumentFormat = DOCUMENT_FORMAT_TEXT;
+ else{
+ ohDocumentFormat = DOCUMENT_FORMAT_AUTO;
+ }
+ /* FIXME 暂时支持PDF格式 */
+ printJob.documentFormat = DOCUMENT_FORMAT_PDF;
+ printJob.advancedOptions = nullptr;
+
+ qWarning() << "start print job page layout" << this->m_pageLayout;
+ /* 启动打印任务 */
+ int ret = OH_Print_StartPrintJob(&printJob);
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "print job execute fail,errCode:" << ret;
+ }
+
+ //QPdfPrintEnginePrivate::closePrintDevice();
+ }
+}
+
+void QOpenHarmonyPrintEnginePrivate::changePrinter(const QString &newPrinter)
+{
+ // Don't waste time if same printer name
+ if (newPrinter == printerName)
+ return;
+
+ // Should never have reached here if no plugin available, but check just in case
+ QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
+ if (!ps)
+ return;
+
+ // Try create the printer, only use it if it returns valid
+ QPrintDevice printDevice = ps->createPrintDevice(newPrinter);
+ if (!printDevice.isValid())
+ return;
+ m_printDevice.swap(printDevice);
+ printerName = m_printDevice.property(PDPK_OHOSPrinterId).toString();
+
+ // in case a duplex value was explicitly set, check if new printer supports current value,
+ // otherwise use device default
+ if (!duplexRequestedExplicitly || !m_printDevice.supportedDuplexModes().contains(duplex)) {
+ duplex = m_printDevice.defaultDuplexMode();
+ duplexRequestedExplicitly = false;
+ }
+ QPrint::ColorMode colorMode = grayscale ? QPrint::GrayScale : QPrint::Color;
+ if (!m_printDevice.supportedColorModes().contains(colorMode))
+ grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale;
+
+ // Get the equivalent page size for this printer as supported names may be different
+ if (m_printDevice.supportedPageSize(m_pageLayout.pageSize()).isValid())
+ setPageSize(m_pageLayout.pageSize());
+ else
+ setPageSize(QPageSize(m_pageLayout.pageSize().size(QPageSize::Point), QPageSize::Point));
+}
+
+void QOpenHarmonyPrintEnginePrivate::setPageSize(const QPageSize &pageSize)
+{
+ if (pageSize.isValid()) {
+ // Find if the requested page size has a matching printer page size, if so use its defined name instead
+ QPageSize printerPageSize = m_printDevice.supportedPageSize(pageSize);
+ QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize;
+ QMarginsF printable = m_printDevice.printableMargins(usePageSize, m_pageLayout.orientation(), resolution);
+ m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units()));
+ }
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYPRINTENGINE_P_H
+#define QOPENHARMONYPRINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtPrintSupport/qprintengine.h"
+
+#include <QtCore/qstring.h>
+#include <QtGui/qpaintengine.h>
+
+#include <private/qpaintengine_p.h>
+#include <private/qprintdevice_p.h>
+#include <private/qprintengine_pdf_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define PPK_OhPrintOptions QPrintEngine::PrintEnginePropertyKey(0xef55)
+
+class QOpenHarmonyPrintEnginePrivate;
+
+class QOpenHarmonyPrintEngine : public QPdfPrintEngine
+{
+ Q_DECLARE_PRIVATE(QOpenHarmonyPrintEngine)
+public:
+ QOpenHarmonyPrintEngine(QPrinter::PrinterMode m, const QString &deviceId);
+ virtual ~QOpenHarmonyPrintEngine();
+
+ // reimplementations QPdfPrintEngine
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value) override;
+ QVariant property(PrintEnginePropertyKey key) const override;
+ // end reimplementations QPdfPrintEngine
+
+private:
+ Q_DISABLE_COPY_MOVE(QOpenHarmonyPrintEngine)
+};
+
+class QOpenHarmonyPrintEnginePrivate : public QPdfPrintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QOpenHarmonyPrintEngine)
+public:
+ QOpenHarmonyPrintEnginePrivate(QPrinter::PrinterMode m);
+ ~QOpenHarmonyPrintEnginePrivate();
+
+ bool openPrintDevice() override;
+ void closePrintDevice() override;
+
+private:
+ Q_DISABLE_COPY_MOVE(QOpenHarmonyPrintEnginePrivate)
+
+ void changePrinter(const QString &newPrinter);
+ void setPageSize(const QPageSize &pageSize);
+
+ QPrintDevice m_printDevice;
+ QStringList ohOptions;
+ QString ohTempFile;
+ QPrint::DuplexMode duplex;
+ bool duplexRequestedExplicitly = false;
+ QPrinter::PrinterMode printMode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYPRINTENGINE_P_H
new file mode 100644
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qopenharmonyprintersupport_p.h"
+
+#include "qopenharmonyprintengine_p.h"
+#include "qopenharmonyprintdevice.h"
+#include <private/qprinterinfo_p.h>
+#include <private/qprintdevice_p.h>
+#include <QtPrintSupport/QPrinterInfo>
+
+#if QT_CONFIG(dialogbuttonbox)
+#include <QGuiApplication>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QLabel>
+#include <QLineEdit>
+#endif // QT_CONFIG(dialogbuttonbox)
+
+QT_BEGIN_NAMESPACE
+#if 0
+#if QT_CONFIG(dialogbuttonbox)
+static const char *getPasswordCB(const char */*prompt*/, http_t *http, const char */*method*/, const char *resource, void */*user_data*/)
+{
+ // cups doesn't free the const char * we return so keep around
+ // the last password so we don't leak memory if called multiple times.
+ static QByteArray password;
+
+ // prompt is always "Password for %s on %s? " but we can't use it since we allow the user to change the user.
+ // That is fine because cups always calls cupsUser after calling this callback.
+ // We build our own prompt with the hostname (if not localhost) and the resource that is being used
+
+ char hostname[HTTP_MAX_HOST];
+ httpGetHostname(http, hostname, HTTP_MAX_HOST);
+
+ const QString username = QString::fromLocal8Bit(cupsUser());
+
+ QDialog dialog;
+ dialog.setWindowTitle(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication Needed"));
+
+ QFormLayout *layout = new QFormLayout(&dialog);
+ layout->setSizeConstraint(QLayout::SetFixedSize);
+
+ QLineEdit *usernameLE = new QLineEdit();
+ usernameLE->setText(username);
+
+ QLineEdit *passwordLE = new QLineEdit();
+ passwordLE->setEchoMode(QLineEdit::Password);
+
+ QString resourceString = QString::fromLocal8Bit(resource);
+ if (resourceString.startsWith(QStringLiteral("/printers/")))
+ resourceString = resourceString.mid(QStringLiteral("/printers/").length());
+
+ QLabel *label = new QLabel();
+ if (hostname == QStringLiteral("localhost")) {
+ label->setText(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication needed to use %1.").arg(resourceString));
+ } else {
+ label->setText(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Authentication needed to use %1 on %2.").arg(resourceString).arg(hostname));
+ label->setWordWrap(true);
+ }
+
+ layout->addRow(label);
+ layout->addRow(new QLabel(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Username:")), usernameLE);
+ layout->addRow(new QLabel(QCoreApplication::translate("QOpenHarmonyPrinterSupport", "Password:")), passwordLE);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ layout->addRow(buttonBox);
+
+ QObject::connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+ QObject::connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+
+ passwordLE->setFocus();
+
+ if (dialog.exec() != QDialog::Accepted)
+ return nullptr;
+
+ if (usernameLE->text() != username)
+ cupsSetUser(usernameLE->text().toLocal8Bit().constData());
+
+ password = passwordLE->text().toLocal8Bit();
+
+ return password.constData();
+}
+#endif // QT_CONFIG(dialogbuttonbox)
+#endif
+
+static QOpenHarmonyPrinterSupport *ps = nullptr;
+
+QOpenHarmonyPrinterSupport::QOpenHarmonyPrinterSupport()
+ : QPlatformPrinterSupport()
+{
+ ps = this;
+ Print_ErrorCode ret = Print_ErrorCode::PRINT_ERROR_NONE;
+
+ ret = OH_Print_Init();
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "QOpenHarmonyPrinterSupport:init Env failed,errCode:" << ret;
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:init Env ok";
+
+ ret = OH_Print_RegisterPrinterChangeListener(Print_PrinterChange);
+ if(PRINT_ERROR_NONE != ret){
+ qWarning() << "QOpenHarmonyPrinterSupport:register printer change failed,errCode:" << ret;
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:register printer change ok";
+
+ ret = OH_Print_StartPrinterDiscovery(Print_PrinterDiscovery);
+ if (PRINT_ERROR_NONE != ret)
+ {
+ qWarning() << "QOpenHarmonyPrinterSupport:StartPrinterDiscovery failed,errCode:" << ret;
+ OH_Print_UnregisterPrinterChangeListener();
+ OH_Print_Release();
+ return;
+ }
+ qInfo() << "QOpenHarmonyPrinterSupport:StartPrinterDiscovery ok";
+
+ detectOHPrinter();
+}
+
+QOpenHarmonyPrinterSupport::~QOpenHarmonyPrinterSupport()
+{
+ OH_Print_StopPrinterDiscovery();
+ OH_Print_UnregisterPrinterChangeListener();
+ OH_Print_Release();
+ ps = nullptr;
+}
+
+QPrintEngine *QOpenHarmonyPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId)
+{
+ return new QOpenHarmonyPrintEngine(printerMode, (deviceId.isEmpty() ? defaultPrintDeviceId() : deviceId));
+}
+
+QPaintEngine *QOpenHarmonyPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrinter::PrinterMode printerMode)
+{
+ Q_UNUSED(printerMode)
+ return static_cast<QOpenHarmonyPrintEngine *>(engine);
+}
+
+QPrintDevice QOpenHarmonyPrinterSupport::createPrintDevice(const QString &id)
+{
+ return QPlatformPrinterSupport::createPrintDevice(new QOpenHarmonyPrintDevice(idFromName(id)));
+}
+
+void QOpenHarmonyPrinterSupport::Print_PrinterChange(Print_PrinterEvent event, const Print_PrinterInfo *printerInfo)
+{
+ qInfo() << "Print_PrinterChange id:" << printerInfo->printerId << " name: " << printerInfo->printerName;
+}
+
+void QOpenHarmonyPrinterSupport::Print_PrinterDiscovery(Print_DiscoveryEvent event, const Print_PrinterInfo *printerInfo)
+{
+ qInfo() << "PrinterDiscovery id:" << printerInfo->printerId << " name: " << printerInfo->printerName;
+ QString id = QString::fromUtf8(printerInfo->printerId);
+ if (event == PRINTER_DISCOVERED) {
+ if (!ps || ps->contains(id))
+ return;
+ OHPrinter p;
+ p.id = id;
+ p.name = QString::fromUtf8(printerInfo->printerName);;
+ p.isDefault = printerInfo->isDefaultPrinter;
+ ps->add(p);
+ } else if (event == PRINTER_LOST) {
+ if (ps != nullptr)
+ ps->remove(id);
+ }
+}
+
+void QOpenHarmonyPrinterSupport::detectOHPrinter()
+{
+ Print_StringList printerIdList;
+ int ret = OH_Print_QueryPrinterList(&printerIdList);
+
+ if (PRINT_ERROR_NONE != ret) {
+ qWarning() << "QOpenHarmonyPrinterSupport:detectOHPrinter OH_Print_QueryPrinterList failed,errCode:" << ret;
+ return;
+ }
+ for (int i = 0; i < printerIdList.count; ++i) {
+ char *id = printerIdList.list[i];
+ Print_PrinterInfo *info = nullptr;
+ ret = OH_Print_QueryPrinterInfo(id, &info);
+ if (PRINT_ERROR_NONE != ret || info == nullptr) {
+ qWarning() << "QOpenHarmonyPrinterSupport:detectOHPrinter OH_Print_QueryPrinterInfo failed,errCode:" << ret;
+ continue;
+ }
+ QString idString = QString::fromUtf8(id);
+ if (contains(idString)) {
+ OH_Print_ReleasePrinterInfo(info);
+ continue;
+ }
+ OHPrinter p;
+ p.id = idString;
+ p.name = QString::fromUtf8(info->printerName);
+ p.isDefault = info->isDefaultPrinter;
+ OH_Print_ReleasePrinterInfo(info);
+ m_printers << p;
+ }
+ OH_Print_ReleasePrinterList(&printerIdList);
+}
+
+bool QOpenHarmonyPrinterSupport::contains(const QString &id)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).id == id)
+ return true;
+ }
+ return false;
+}
+
+QStringList QOpenHarmonyPrinterSupport::availablePrintDeviceIds() const
+{
+ QStringList result;
+ for (int i = 0; i < m_printers.count(); ++i) {
+ result << m_printers.at(i).name;
+ }
+ return result;
+}
+
+QString QOpenHarmonyPrinterSupport::defaultPrintDeviceId() const
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).isDefault)
+ return m_printers.at(i).name;
+ }
+ return QString();
+}
+
+void QOpenHarmonyPrinterSupport::add(const OHPrinter &p)
+{
+ m_printers << p;
+}
+
+void QOpenHarmonyPrinterSupport::remove(const QString &id)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).id == id) {
+ m_printers.removeAt(i);
+ break;
+ }
+ }
+}
+
+QString QOpenHarmonyPrinterSupport::defaultPrinterId() const
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).isDefault)
+ return m_printers.at(i).id;
+ }
+ return QString();
+}
+
+QString QOpenHarmonyPrinterSupport::idFromName(const QString &name)
+{
+ for (int i = 0; i < m_printers.count(); ++i) {
+ if (m_printers.at(i).name == name)
+ return m_printers.at(i).id;
+ }
+ return QString();
+}
+
+QT_END_NAMESPACE
new file mode 100644
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 John Layt <jlayt@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOPENHARMONYSPRINTERSUPPORT_H
+#define QOPENHARMONYSPRINTERSUPPORT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qpa/qplatformprintersupport.h>
+
+#include <QtCore/qstringlist.h>
+#include <BasicServicesKit/ohprint.h>
+
+QT_BEGIN_NAMESPACE
+
+struct OHPrinter
+{
+ QString name;
+ QString id;
+ bool isDefault;
+};
+
+class QOpenHarmonyPrinterSupport : public QPlatformPrinterSupport
+{
+public:
+ QOpenHarmonyPrinterSupport();
+ ~QOpenHarmonyPrinterSupport();
+
+ QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override;
+ QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) override;
+
+ QPrintDevice createPrintDevice(const QString &id) override;
+ QStringList availablePrintDeviceIds() const override;
+ QString defaultPrintDeviceId() const override;
+ void add(const OHPrinter &p);
+ void remove(const QString &id);
+private:
+ QString defaultPrinterId() const;
+ QString idFromName(const QString &name);
+ void detectOHPrinter();
+ bool contains(const QString &id);
+ QString cupsOption(int i, const QString &key) const;
+
+ static void Print_PrinterChange(Print_PrinterEvent event, const Print_PrinterInfo *printerInfo);
+ static void Print_PrinterDiscovery(Print_DiscoveryEvent event, const Print_PrinterInfo *printerInfo);
+ QList<OHPrinter> m_printers;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENHARMONYSPRINTERSUPPORT_H
@@ -24,6 +24,9 @@
# include <android/log.h>
#endif
+#ifdef Q_OS_OPENHARMONY
+#include <QDebug>
+#endif
#ifdef Q_OS_WIN
# include <qt_windows.h>
#endif
@@ -249,6 +252,9 @@ void QPlainTestLogger::outputMessage(const char *str)
}
#elif defined(Q_OS_ANDROID)
__android_log_write(ANDROID_LOG_INFO, "QTestLib", str);
+#elif defined(Q_OS_OPENHARMONY)
+ /* wanghao TODO */
+ qInfo() << str;
#endif
outputString(str);
}
@@ -115,6 +115,12 @@
#include <CoreFoundation/CFPreferences.h>
#endif
+#ifdef Q_OS_OPENHARMONY
+#include <QtCore/QLoggingCategory>
+Q_LOGGING_CATEGORY(openharmonyTestCase, "openharmony.testcase");
+#endif
+
+
#include <vector>
QT_BEGIN_NAMESPACE
@@ -1753,10 +1759,19 @@ void TestMethods::invokeTests(QObject *testObject) const
QTestResult::finishedCurrentTestFunction();
if (!QTestResult::skipCurrentTest() && !previousFailed) {
+#ifdef Q_OS_OPENHARMONY
+ const char *className = metaObject->className();
+ int size = m_methods.size();
+#endif
for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
const char *data = nullptr;
if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
+#ifdef Q_OS_OPENHARMONY
+ qCWarning(openharmonyTestCase) << "Now Test Class:" << className
+ << ", Now Test Func:" << m_methods[i].name().constData()
+ << ", The Func Progress:" << (i + 1) << "/" << size;
+#endif
const bool ok = invokeTest(i, QLatin1StringView(data), watchDog.data());
delete [] data;
if (!ok)
@@ -37,6 +37,12 @@
#include <vector>
#include <memory>
+#ifdef Q_OS_OPENHARMONY
+#include "qcoreapplication.h"
+#include <hilog/log.h>
+#define APP_LOG_DOMAIN 0xf000
+#define APP_LOG_TAG "QtForOpenHarmony"
+#endif
QT_BEGIN_NAMESPACE
@@ -234,8 +240,24 @@ namespace QTest {
}
}
+#ifdef Q_OS_OPENHARMONY
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+
+ LogLevel priority = LOG_INFO;
+ switch (type) {
+ //LOG_DEBUG unable to print, temporarily use LOG_ INFO replace.
+ case QtDebugMsg: priority = LOG_INFO; break;
+ case QtInfoMsg: priority = LOG_INFO; break;
+ case QtWarningMsg: priority = LOG_WARN; break;
+ case QtCriticalMsg: priority = LOG_ERROR; break;
+ case QtFatalMsg: priority = LOG_FATAL; break;
+ };
+
+ OH_LOG_Print(LOG_APP, priority, APP_LOG_DOMAIN, APP_LOG_TAG, "%{public}s %{public}s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(formattedMessage));
+#else
FOREACH_TEST_LOGGER
logger->addMessage(type, context, message);
+#endif
if (type == QtFatalMsg) {
/* Right now, we're inside the custom message handler and we're
@@ -320,6 +320,11 @@ qt_internal_extend_target(Widgets CONDITION WIN32
uxtheme
)
+qt_internal_extend_target(Widgets CONDITION OHOS
+ LIBRARIES
+ hitrace_ndk.z
+)
+
qt_internal_extend_target(Widgets CONDITION QT_FEATURE_graphicseffect
SOURCES
effects/qgraphicseffect.cpp effects/qgraphicseffect.h effects/qgraphicseffect_p.h
@@ -1075,7 +1075,7 @@ QStringList QAccessibleTableCell::actionNames() const
void QAccessibleTableCell::doAction(const QString& actionName)
{
if (actionName == toggleAction()) {
-#if defined(Q_OS_ANDROID)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_OPENHARMONY)
QAccessibleInterface *parentInterface = parent();
while (parentInterface){
if (parentInterface->role() == QAccessible::ComboBox) {
@@ -44,6 +44,11 @@
#include <private/qguiapplication_p.h>
#include <algorithm>
+#ifdef Q_OS_OPENHARMONY
+#include "qjsmodule.h"
+#include <private/qjspromise_p.h>
+#include <private/qopenharmony_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -118,7 +123,9 @@ public:
bool handleColorPickingMouseMove(QMouseEvent *e);
bool handleColorPickingMouseButtonRelease(QMouseEvent *e);
bool handleColorPickingKeyPress(QKeyEvent *e);
-
+#ifdef Q_OS_OPENHARMONY
+ void imageFeaturePicker(QColorDialog *window);
+#endif
bool canBeNativeDialog() const override;
void setVisible(bool visible) override;
@@ -1609,7 +1616,9 @@ void QColorDialogPrivate::_q_newStandard(int r, int c)
void QColorDialogPrivate::_q_pickScreenColor()
{
Q_Q(QColorDialog);
-
+#ifdef Q_OS_OPENHARMONY
+ imageFeaturePicker(q);
+#else
auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services();
if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) {
if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) {
@@ -1657,6 +1666,7 @@ void QColorDialogPrivate::_q_pickScreenColor()
q->setCurrentColor(grabScreenColor(globalPos));
updateColorLabelText(globalPos);
}
+#endif
}
void QColorDialogPrivate::updateColorLabelText(const QPoint &globalPos)
@@ -2294,6 +2304,56 @@ bool QColorDialogPrivate::handleColorPickingKeyPress(QKeyEvent *e)
return true;
}
+#ifdef Q_OS_OPENHARMONY
+void QColorDialogPrivate::imageFeaturePicker(QColorDialog *dialog)
+{
+ if (!dialog) {
+ return;
+ }
+ QPoint center;
+ QScreen *screen = dialog->screen();
+ if (screen) {
+ QRect screenGeometry = screen->geometry();
+ qreal dpr = screen->devicePixelRatio();
+
+ center = screenGeometry.center();
+ center = QPoint(center.x() * dpr, center.y() * dpr);
+ }
+ else
+ {
+ return;
+ }
+
+ auto pickeColor = QtOh::runOnJsUIThreadWithPromise<QColor>([center](auto p){
+ auto imageFeaturePicker = QJsModule(QString::fromUtf8("@hms.officeservice.imageFeaturePicker"));
+ Napi::Value result = imageFeaturePicker.call(QString::fromUtf8("pickForResult"),
+ { Napi::Number::New(imageFeaturePicker.env(),center.x()),
+ Napi::Number::New(imageFeaturePicker.env(),center.y())});
+ if (!result.IsPromise()) {
+ p->set_value(Qt::white);
+ return;
+ }
+ QJsPromise promise(result.As<Napi::Promise>());
+ promise.onThen([p](const Napi::CallbackInfo& info){
+ if (info.Length() < 1) {
+ p->set_value(Qt::white);
+ return ;
+ }
+ Napi::Object PickedColorInfo = info[0].As<Napi::Object>();
+ Napi::Object color = PickedColorInfo.Get("color").As<Napi::Object>();
+ QColor pickeColor(color.Get("red").As<Napi::Number>().Int32Value(),
+ color.Get("green").As<Napi::Number>().Int32Value(),
+ color.Get("blue").As<Napi::Number>().Int32Value(),
+ color.Get("alpha").As<Napi::Number>().Int32Value());
+
+ p->set_value(pickeColor);
+
+ }).onCatch([p](const Napi::CallbackInfo& info){ Q_UNUSED(info); p->set_value(Qt::white); });
+ });
+ dialog->setCurrentColor(pickeColor);
+}
+#endif
+
/*!
Closes the dialog and sets its result code to \a result. If this dialog
is shown with exec(), done() causes the local event loop to finish,
@@ -1222,6 +1222,16 @@ QList<QUrl> QFileDialogPrivate::userSelectedFiles() const
return files;
}
+#ifdef Q_OS_OPENHARMONY
+QList<QUrl> QFileDialogPrivate::userSelectedUris() const
+{
+ if (!usingWidgets())
+ return addDefaultSuffixToUrls(selectedUris_sys());
+
+ return QList<QUrl>();
+}
+#endif
+
QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList &filesToFix) const
{
QStringList files;
@@ -1316,6 +1326,18 @@ QList<QUrl> QFileDialog::selectedUrls() const
}
}
+#ifdef Q_OS_OPENHARMONY
+QList<QUrl> QFileDialog::selectedUris() const
+{
+ Q_D(const QFileDialog);
+ if (d->nativeDialogInUse) {
+ return d->userSelectedUris();
+ }
+
+ return QList<QUrl>();
+}
+#endif
+
/*
Makes a list of filters from ;;-separated text.
Used by the mac and windows implementations
@@ -2144,6 +2166,35 @@ QUrl QFileDialog::getOpenFileUrl(QWidget *parent,
return QUrl();
}
+#ifdef Q_OS_OPENHARMONY
+QUrl QFileDialog::getOpenFileUri(QWidget *parent,
+ const QString &caption,
+ const QUrl &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options,
+ const QStringList &supportedSchemes)
+{
+ QFileDialogArgs args(dir);
+ args.parent = parent;
+ args.caption = caption;
+ args.filter = filter;
+ args.mode = ExistingFile;
+ args.options = options;
+
+ QFileDialog dialog(args);
+ dialog.setSupportedSchemes(supportedSchemes);
+ if (selectedFilter && !selectedFilter->isEmpty())
+ dialog.selectNameFilter(*selectedFilter);
+ if (dialog.exec() == QDialog::Accepted) {
+ if (selectedFilter)
+ *selectedFilter = dialog.selectedNameFilter();
+ return dialog.selectedUris().value(0);
+ }
+ return QUrl();
+}
+#endif
+
/*!
This is a convenience static function that will return one or more existing
files selected by the user.
@@ -2914,8 +2965,9 @@ void QFileDialogPrivate::init(const QFileDialogArgs &args)
if (!nativeDialogInUse)
createWidgets();
q->setFileMode(QFileDialog::AnyFile);
- if (!args.filter.isEmpty())
+ if (!args.filter.isEmpty()){
q->setNameFilter(args.filter);
+ }
// QTBUG-70798, prevent the default blocking the restore logic.
const bool dontStoreDir = !args.directory.isValid() && !lastVisitedDir()->isValid();
q->setDirectoryUrl(args.directory);
@@ -77,6 +77,9 @@ public:
void selectUrl(const QUrl &url);
QList<QUrl> selectedUrls() const;
+#ifdef Q_OS_OPENHARMONY
+ QList<QUrl> selectedUris() const;
+#endif
void setNameFilter(const QString &filter);
void setNameFilters(const QStringList &filters);
@@ -171,6 +174,16 @@ public:
Options options = Options(),
const QStringList &supportedSchemes = QStringList());
+#ifdef Q_OS_OPENHARMONY
+ static QUrl getOpenFileUri(QWidget *parent = nullptr,
+ const QString &caption = QString(),
+ const QUrl &dir = QUrl(),
+ const QString &filter = QString(),
+ QString *selectedFilter = nullptr,
+ Options options = Options(),
+ const QStringList &supportedSchemes = QStringList());
+#endif
+
static QString getSaveFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
@@ -101,6 +101,9 @@ public:
QString getEnvironmentVariable(const QString &string);
QStringList typedFiles() const;
QList<QUrl> userSelectedFiles() const;
+#ifdef Q_OS_OPENHARMONY
+ QList<QUrl> userSelectedUris() const;
+#endif
QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const;
QList<QUrl> addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const;
bool removeDirectory(const QString &path);
@@ -228,6 +231,9 @@ public:
inline QUrl directory_sys() const;
inline void selectFile_sys(const QUrl &filename);
inline QList<QUrl> selectedFiles_sys() const;
+#ifdef Q_OS_OPENHARMONY
+ inline QList<QUrl> selectedUris_sys() const;
+#endif
inline void setFilter_sys();
inline void selectMimeTypeFilter_sys(const QString &filter);
inline QString selectedMimeTypeFilter_sys() const;
@@ -372,6 +378,15 @@ QList<QUrl> QFileDialogPrivate::selectedFiles_sys() const
return QList<QUrl>();
}
+#ifdef Q_OS_OPENHARMONY
+QList<QUrl> QFileDialogPrivate::selectedUris_sys() const
+{
+ if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
+ return helper->selectedUris();
+ return QList<QUrl>();
+}
+#endif
+
void QFileDialogPrivate::setFilter_sys()
{
if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
@@ -497,6 +497,9 @@ static bool removeWidgetRecursively(QLayoutItem *li, QObject *w)
void QLayoutPrivate::doResize()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QLayoutPrivate::doResize");
+#endif
Q_Q(QLayout);
QWidget *mw = q->parentWidget();
QRect rect = mw->testAttribute(Qt::WA_LayoutOnEntireRect) ? mw->rect() : mw->contentsRect();
@@ -993,6 +996,9 @@ void QLayout::update()
*/
bool QLayout::activate()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QLayout::activate");
+#endif
Q_D(QLayout);
if (!d->enabled || !parent())
return false;
@@ -161,14 +161,26 @@ QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state,
} else {
d->lastCenterPoint = d->centerPoint;
}
+#ifdef Q_OS_OPENHARMONY
+ if (d->centerPoint != centerPoint) {
+ d->centerPoint = centerPoint;
+ d->changeFlags |= QPinchGesture::CenterPointChanged;
+ }
+#else
d->centerPoint = centerPoint;
d->changeFlags |= QPinchGesture::CenterPointChanged;
-
+#endif
if (d->isNewSequence) {
d->scaleFactor = 1.0;
d->lastScaleFactor = 1.0;
} else {
+#ifdef Q_OS_OPENHARMONY
+ if (!qFuzzyCompare(1.0 + d->lastScaleFactor, 1.0 + d->scaleFactor)) {
+ d->totalScaleFactor = d->totalScaleFactor * d->scaleFactor;
+ d->changeFlags |= QPinchGesture::ScaleFactorChanged;
+ }
+#endif
d->lastScaleFactor = d->scaleFactor;
QLineF line(p1.globalPosition(), p2.globalPosition());
QLineF lastLine(p1.globalLastPosition(), p2.globalLastPosition());
@@ -177,9 +189,10 @@ QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state,
return QGestureRecognizer::Ignore;
d->scaleFactor = newScaleFactor;
}
+#ifndef Q_OS_OPENHARMONY
d->totalScaleFactor = d->totalScaleFactor * d->scaleFactor;
d->changeFlags |= QPinchGesture::ScaleFactorChanged;
-
+#endif
qreal angle = QLineF(p1.globalPosition(), p2.globalPosition()).angle();
if (angle > 180)
angle -= 360;
@@ -190,11 +203,22 @@ QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state,
if (d->isNewSequence)
d->lastRotationAngle = 0.0;
else
+#ifdef Q_OS_OPENHARMONY
+ {
+ if (!qFuzzyCompare(1.0 + d->lastRotationAngle, 1.0 + d->rotationAngle)) {
+ d->lastRotationAngle = d->rotationAngle;
+ d->totalRotationAngle += d->rotationAngle - d->lastRotationAngle;
+ d->changeFlags |= QPinchGesture::RotationAngleChanged;
+ }
+ }
+#else
d->lastRotationAngle = d->rotationAngle;
+#endif
d->rotationAngle = rotationAngle;
+#ifndef Q_OS_OPENHARMONY
d->totalRotationAngle += d->rotationAngle - d->lastRotationAngle;
d->changeFlags |= QPinchGesture::RotationAngleChanged;
-
+#endif
d->totalChangeFlags |= d->changeFlags;
d->isNewSequence = false;
result = QGestureRecognizer::TriggerGesture;
@@ -372,7 +372,11 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w)
p += offset;
+#ifdef Q_OS_OPENHARMONY
+ QRect screenRect = screen->availableGeometry();
+#else
QRect screenRect = screen->geometry();
+#endif
if (p.x() + this->width() > screenRect.x() + screenRect.width())
p.rx() -= 4 + this->width();
if (p.y() + this->height() > screenRect.y() + screenRect.height())
@@ -1155,6 +1155,9 @@ bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outCo
void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidget::create");
+#endif
Q_UNUSED(initializeWindow);
Q_UNUSED(destroyOldWindow);
@@ -1754,6 +1757,9 @@ QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst
void QWidgetPrivate::syncBackingStore()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::syncBackingStore");
+#endif
if (shouldPaintOnScreen()) {
paintOnScreen(dirty);
dirty = QRegion();
@@ -4786,6 +4792,9 @@ void QWidgetPrivate::resolveFont()
*/
void QWidgetPrivate::updateFont(const QFont &font)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::updateFont");
+#endif
Q_Q(QWidget);
#ifndef QT_NO_STYLE_STYLESHEET
const QStyleSheetStyle* cssStyle;
@@ -5455,6 +5464,9 @@ void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset
void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, DrawWidgetFlags flags,
QPainter *sharedPainter, QWidgetRepaintManager *repaintManager)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::drawWidget");
+#endif
if (rgn.isEmpty())
return;
@@ -5641,6 +5653,9 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("sendPaintEvent");
+#endif
Q_Q(QWidget);
QPaintEvent e(toBePainted);
QCoreApplication::sendSpontaneousEvent(q, &e);
@@ -5725,6 +5740,9 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis
const QPoint &offset, DrawWidgetFlags flags
, QPainter *sharedPainter, QWidgetRepaintManager *repaintManager)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("paintSiblingsRecursive");
+#endif
QWidget *w = nullptr;
QRect boundingRect;
bool dirtyBoundingRect = true;
@@ -8030,6 +8048,9 @@ void QWidgetPrivate::activateChildLayoutsRecursively()
void QWidgetPrivate::show_helper()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::show_helper");
+#endif
Q_Q(QWidget);
data.in_show = true; // qws optimization
// make sure we receive pending move and resize events
@@ -8114,6 +8135,9 @@ void QWidgetPrivate::show_helper()
void QWidgetPrivate::show_sys()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::show_sys");
+#endif
Q_Q(QWidget);
auto window = qobject_cast<QWidgetWindow *>(windowHandle());
@@ -8322,6 +8346,9 @@ void QWidget::setVisible(bool visible)
// the other hand keeps track of WA_WState_ExplicitShowHide in addition.
void QWidgetPrivate::setVisible(bool visible)
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetPrivate::setVisible");
+#endif
Q_Q(QWidget);
if (visible) { // show
// Designer uses a trick to make grabWidget work without showing
@@ -8536,7 +8563,17 @@ bool QWidgetPrivate::handleClose(CloseMode mode)
mode = CloseNoEvent;
if (mode != CloseNoEvent) {
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ QVariant reason = that->property("_qohclosereason");
+ QCloseEvent e(reason.isValid() ? QCloseEvent::CloseReason(reason.toInt()) : QCloseEvent::InternalClose);
+#else
+ QCloseEvent e;
+#endif
+#else
QCloseEvent e;
+#endif
+
if (mode == CloseWithSpontaneousEvent)
QApplication::sendSpontaneousEvent(q, &e);
else
@@ -11101,6 +11138,9 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r)
void QWidget::repaint()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidget::repaint");
+#endif
repaint(rect());
}
@@ -11179,6 +11219,9 @@ void QWidgetPrivate::repaint(T r)
*/
void QWidget::update()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidget::update");
+#endif
update(rect());
}
@@ -361,7 +361,12 @@ void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime update
// normal backingstore sync machinery.
if (!widget->d_func()->shouldPaintOnScreen())
updateRequestSent = true;
+#ifdef Q_OS_OPENHARMONY
+ // most important on openharmony
+ QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::HighEventPriority + 1);
+#else
QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
+#endif
break;
case UpdateNow: {
QEvent event(QEvent::UpdateRequest);
@@ -703,6 +708,9 @@ static bool isDrawnInEffect(const QWidget *w)
void QWidgetRepaintManager::paintAndFlush()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetRepaintManager::paintAndFlush");
+#endif
qCInfo(lcWidgetPainting) << "Painting and flushing dirty"
<< "top level" << dirty << "and dirty widgets" << dirtyWidgets;
@@ -901,6 +909,9 @@ void QWidgetRepaintManager::paintAndFlush()
// Paint the rest with composition.
if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer2("Paint the rest with composition");
+#endif
QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive
| QWidgetPrivate::UseEffectRegionBounds;
tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, nullptr, this);
@@ -968,6 +979,9 @@ void QWidgetRepaintManager::markNeedsFlush(QWidget *widget, const QRegion ®io
*/
void QWidgetRepaintManager::flush()
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("QWidgetRepaintManager::flush");
+#endif
qCInfo(lcWidgetPainting) << "Flushing top level"
<< topLevelNeedsFlush << "and children" << needsFlushWidgets;
@@ -819,6 +819,11 @@ void QWidgetWindow::handleResizeEvent(QResizeEvent *event)
void QWidgetWindow::closeEvent(QCloseEvent *event)
{
Q_D(QWidgetWindow);
+#ifdef Q_OS_OPENHARMONY
+#if OHOS_SDK_VERSION >= 15
+ m_widget->setProperty("_qohclosereason", event->closeReason());
+#endif
+#endif
bool accepted = m_widget->d_func()->handleClose(d->inClose ? QWidgetPrivate::CloseWithEvent
: QWidgetPrivate::CloseWithSpontaneousEvent);
event->setAccepted(accepted);
@@ -1124,10 +1129,15 @@ void QWidgetWindow::handleGestureEvent(QNativeGestureEvent *e)
#ifndef QT_NO_CONTEXTMENU
void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e)
{
+#ifdef Q_OS_OPENHARMONY
+ if (e->reason() != QContextMenuEvent::TouchScreen)
+ return;
+#else
// We are only interested in keyboard originating context menu events here,
// mouse originated context menu events for widgets are generated in mouse handling methods.
if (e->reason() != QContextMenuEvent::Keyboard)
return;
+#endif
QWidget *fw = QWidget::keyboardGrabber();
if (!fw) {
@@ -1143,8 +1153,13 @@ void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e)
}
if (fw && fw->isEnabled()) {
QPoint pos = fw->inputMethodQuery(Qt::ImCursorRectangle).toRect().center();
+#ifdef Q_OS_OPENHARMONY
+ QContextMenuEvent widgetEvent(QContextMenuEvent::TouchScreen, pos, fw->mapToGlobal(pos),
+ e->modifiers());
+#else
QContextMenuEvent widgetEvent(QContextMenuEvent::Keyboard, pos, fw->mapToGlobal(pos),
e->modifiers());
+#endif
QGuiApplication::forwardEvent(fw, &widgetEvent, e);
}
}
@@ -187,6 +187,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
}
break; }
case PE_FrameFocusRect:
+#ifndef Q_OS_OPENHARMONY
if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
QColor bg = fropt->backgroundColor;
QPen oldPen = p->pen();
@@ -204,6 +205,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive
p->setPen(oldPen);
}
+#endif
break;
case PE_IndicatorMenuCheckMark: {
const int markW = opt->rect.width() > 7 ? 7 : opt->rect.width();
@@ -574,7 +576,9 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
#endif // QT_CONFIG(spinbox)
case PE_PanelTipLabel: {
const QBrush brush(opt->palette.toolTipBase());
+#ifndef Q_OS_OPENHARMONY
qDrawPlainRect(p, opt->rect, opt->palette.toolTipText().color(), 1, &brush);
+#endif
break;
}
#if QT_CONFIG(tabbar)
@@ -533,13 +533,17 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid
ret = 0;
break;
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN) || defined(Q_OS_OPENHARMONY)
case SH_UnderlineShortcut:
{
+#ifdef Q_OS_OPENHARMONY
+ ret = 0;
+#else
ret = 1;
BOOL cues = false;
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &cues, 0);
ret = int(cues);
+#endif
// Do nothing if we always paint underlines
Q_D(const QWindowsStyle);
if (!ret && widget && d) {
@@ -137,6 +137,15 @@ QSystemTrayIcon::QSystemTrayIcon(const QIcon &icon, QObject *parent)
setIcon(icon);
}
+#ifdef Q_OS_OPENHARMONY
+QSystemTrayIcon::QSystemTrayIcon(const QIcon &iconLight, const QIcon &iconDark, QObject *parent)
+ : QSystemTrayIcon(parent)
+{
+ setIcon(iconLight, Light);
+ setIcon(iconDark, Dark);
+}
+#endif
+
/*!
Removes the icon from the system tray and frees all allocated resources.
*/
@@ -202,6 +211,26 @@ QMenu* QSystemTrayIcon::contextMenu() const
On Windows, the system tray icon size is 16x16; on X11, the preferred size is
22x22. The icon will be scaled to the appropriate size as necessary.
*/
+#ifdef Q_OS_OPENHARMONY
+void QSystemTrayIcon::setIcon(const QIcon &icon, IconTheme them)
+{
+ Q_D(QSystemTrayIcon);
+ if (them == Light)
+ d->iconLight = icon;
+ else
+ d->iconDark = icon;
+ d->updateIcon_sys();
+}
+
+QIcon QSystemTrayIcon::icon(IconTheme them) const
+{
+ Q_D(const QSystemTrayIcon);
+ if (them == Light)
+ return d->iconLight;
+ else
+ return d->iconDark;
+}
+#else
void QSystemTrayIcon::setIcon(const QIcon &icon)
{
Q_D(QSystemTrayIcon);
@@ -214,6 +243,7 @@ QIcon QSystemTrayIcon::icon() const
Q_D(const QSystemTrayIcon);
return d->icon;
}
+#endif
/*!
\property QSystemTrayIcon::toolTip
@@ -277,7 +307,11 @@ void QSystemTrayIcon::setVisible(bool visible)
Q_D(QSystemTrayIcon);
if (visible == d->visible)
return;
+#ifdef Q_OS_OPENHARMONY
+ if (Q_UNLIKELY(visible && d->iconLight.isNull() && d->iconDark.isNull()))
+#else
if (Q_UNLIKELY(visible && d->icon.isNull()))
+#endif
qWarning("QSystemTrayIcon::setVisible: No Icon set");
d->visible = visible;
if (d->visible)
@@ -32,6 +32,9 @@ class Q_WIDGETS_EXPORT QSystemTrayIcon : public QObject
public:
QSystemTrayIcon(QObject *parent = nullptr);
QSystemTrayIcon(const QIcon &icon, QObject *parent = nullptr);
+#ifdef Q_OS_OPENHARMONY
+ QSystemTrayIcon(const QIcon &iconLight, const QIcon &iconDark, QObject *parent = nullptr);
+#endif
~QSystemTrayIcon();
enum ActivationReason {
@@ -42,13 +45,25 @@ public:
MiddleClick
};
+#ifdef Q_OS_OPENHARMONY
+ enum IconTheme {
+ Dark,
+ Light
+ };
+#endif
+
#if QT_CONFIG(menu)
void setContextMenu(QMenu *menu);
QMenu *contextMenu() const;
#endif
+#ifdef Q_OS_OPENHARMONY
+ QIcon icon(IconTheme them = Light) const;
+ void setIcon(const QIcon &icon, IconTheme them = Light);
+#else
QIcon icon() const;
void setIcon(const QIcon &icon);
+#endif
QString toolTip() const;
void setToolTip(const QString &tip);
@@ -63,7 +63,12 @@ public:
void _q_emitActivated(QPlatformSystemTrayIcon::ActivationReason reason);
QPointer<QMenu> menu;
+#ifdef Q_OS_OPENHARMONY
+ QIcon iconLight;
+ QIcon iconDark;
+#else
QIcon icon;
+#endif
QString toolTip;
QSystemTrayIconSys *sys;
QPlatformSystemTrayIcon *qpa_sys;
@@ -53,7 +53,11 @@ QRect QSystemTrayIconPrivate::geometry_sys() const
void QSystemTrayIconPrivate::updateIcon_sys()
{
if (qpa_sys)
+#ifdef Q_OS_OPENHARMONY
+ qpa_sys->updateIcon(iconLight, iconDark);
+#else
qpa_sys->updateIcon(icon);
+#endif
}
void QSystemTrayIconPrivate::updateMenu_sys()
@@ -1002,7 +1002,7 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
&& (event->position().toPoint() - state->pressPos).manhattanLength()
> QApplication::startDragDistance()) {
-#ifdef Q_OS_MACOS
+#if defined(Q_OS_MACOS) || defined(Q_OS_OPENHARMONY)
if (windowHandle() && !q->isFloating()) {
// When using native widgets on mac, we have not yet been successful in
// starting a drag on an NSView that belongs to one window (QMainWindow),
@@ -1130,13 +1130,13 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
if (state == nullptr || !state->dragging)
break;
-#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
+#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM) && !defined(Q_OS_OPENHARMONY)
if (state->nca)
endDrag(EndDragMode::LocationChange);
#endif
break;
case QEvent::NonClientAreaMouseButtonRelease:
-#if defined(Q_OS_MAC) || defined(Q_OS_WASM)
+#if defined(Q_OS_MAC) || defined(Q_OS_WASM) || defined(Q_OS_OPENHARMONY)
if (state)
endDrag(EndDragMode::LocationChange);
#endif
@@ -2903,8 +2903,13 @@ void QMenu::mousePressEvent(QMouseEvent *e)
}
QMenuPrivate::mouseDown = this;
- QAction *action = d->actionAt(e->position().toPoint());
+ QAction *action = d->actionAt(e->pos());
+#ifdef Q_OS_OPENHARMONY
+ /* NOTE 延迟窗口创建,避免touch事件传递到错误窗口 */
+ d->setCurrentAction(action, 200);
+#else
d->setCurrentAction(action, 20);
+#endif
update();
}
@@ -705,6 +705,9 @@ QAction *QMenuBarPrivate::getNextAction(const int _start, const int increment) c
*/
QMenuBar::QMenuBar(QWidget *parent) : QWidget(*new QMenuBarPrivate, parent, { })
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("new QMenuBar");
+#endif
Q_D(QMenuBar);
d->init();
}
@@ -191,6 +191,9 @@ QRect QStatusBarPrivate::messageRect() const
QStatusBar::QStatusBar(QWidget * parent)
: QWidget(*new QStatusBarPrivate, parent, { })
{
+#ifdef Q_OS_OPENHARMONY
+ HiTracer tracer("new QStatusBar");
+#endif
Q_D(QStatusBar);
d->box = nullptr;
d->timer = nullptr;
@@ -144,6 +144,11 @@ QString QTextBrowserPrivate::findFile(const QUrl &name) const
if (name.scheme() == "assets"_L1)
fileName = "assets:"_L1 + name.path();
else
+#endif
+#if defined(Q_OS_OPENHARMONY)
+ if (name.scheme() == "rawfile"_L1)
+ fileName = "rawfile:"_L1 + name.path();
+ else
#endif
fileName = name.toLocalFile();
}
@@ -215,7 +220,10 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href)
#if defined(Q_OS_ANDROID)
|| url.scheme() == "assets"_L1
#endif
- || url.scheme() == "qrc"_L1;
+#if defined(Q_OS_OPENHARMONY)
+ || url.scheme() == "rawfile"_L1
+#endif
+ || url.scheme() == QLatin1String("qrc");
if ((openExternalLinks && !isFileScheme && !url.isRelative())
|| (url.isRelative() && !currentURL.isRelative() && !isFileScheme)) {
QDesktopServices::openUrl(url);
@@ -33,6 +33,7 @@
"QtMockStaticResources1" => "$basedir/tests/auto/cmake/test_static_resources/mock_static_resources1",
"QtTestAutogeneratingCppExports" => "$basedir/tests/auto/cmake/test_generating_cpp_exports/test_autogenerating_cpp_exports",
"QtTestAutogeneratingCppExportsCustomName" => "$basedir/tests/auto/cmake/test_generating_cpp_exports/test_autogenerating_cpp_exports_custom_name",
+ "QtNapi" => "!>$basedir/src/corelib;$basedir/src/3rdparty/node-addon-api",
);
%moduleheaders = ( # restrict the module headers to those found in relative path
"QtEglFSDeviceIntegration" => "api",
@@ -76,8 +77,9 @@
my @internal_zlib_headers = ( "crc32.h", "deflate.h", "gzguts.h", "inffast.h", "inffixed.h", "inflate.h", "inftrees.h", "trees.h", "zutil.h" );
my @zlib_headers = ( "zconf.h", "zlib.h" );
@ignore_headers = ( @internal_zlib_headers );
-@ignore_for_include_check = ( "qsystemdetection.h", "qcompilerdetection.h", "qprocessordetection.h", @zlib_headers);
-@ignore_for_qt_begin_namespace_check = ( "qt_windows.h", @zlib_headers);
+my @napi_headers = ( "napi.h", "napi-inl.h", "napi-inl.deprecated.h" );
+@ignore_for_include_check = ( "qsystemdetection.h", "qcompilerdetection.h", "qprocessordetection.h", @zlib_headers, @napi_headers);
+@ignore_for_qt_begin_namespace_check = ( "qt_windows.h", @zlib_headers, @napi_headers);
%inject_headers = (
"$basedir/src/corelib/global" => [ "qconfig.h", "qconfig_p.h" ],
"$basedir/src/gui/vulkan" => [ "^qvulkanfunctions.h", "^qvulkanfunctions_p.h" ]
@@ -313,7 +313,7 @@ void tst_QSettings::cleanupTestFiles()
QSettings("HKEY_LOCAL_MACHINE\\Software\\bat", QSettings::NativeFormat).clear();
QSettings("HKEY_LOCAL_MACHINE\\Software\\baz", QSettings::NativeFormat).clear();
}
-#elif defined(Q_OS_DARWIN)
+#elif defined(Q_OS_DARWIN)|| defined(Q_OS_OPENHARMONY)
QSettings(QSettings::UserScope, "software.org", "KillerAPP").clear();
QSettings(QSettings::SystemScope, "software.org", "KillerAPP").clear();
QSettings(QSettings::UserScope, "other.software.org", "KillerAPP").clear();
@@ -2340,7 +2340,7 @@ void tst_QSettings::fromFile()
QCOMPARE(settings2.value("alpha").toInt(), 2);
settings1.sync();
-#if !defined(Q_OS_WIN)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_OPENHARMONY)
QVERIFY(QFile::exists("foo"));
#endif
QCOMPARE(settings1.value("alpha").toInt(), 2);
@@ -3310,7 +3310,7 @@ void tst_QSettings::setPath()
path checks that it has no bad side effects.
*/
for (int i = 0; i < 2; ++i) {
-#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_OPENHARMONY)
TEST_PATH(i == 0, "conf", NativeFormat, UserScope, "alpha")
TEST_PATH(i == 0, "conf", NativeFormat, SystemScope, "beta")
#endif