Merge integration refs/builds/qtci/dev/1618585460
This commit is contained in:
commit
5839dee30f
@ -17,9 +17,10 @@
|
|||||||
function(qt_internal_add_module target)
|
function(qt_internal_add_module target)
|
||||||
qt_internal_module_info(module "${target}")
|
qt_internal_module_info(module "${target}")
|
||||||
|
|
||||||
|
# TODO: Remove GENERATE_METATYPES once it's not used anymore.
|
||||||
# Process arguments:
|
# Process arguments:
|
||||||
qt_parse_all_arguments(arg "qt_add_module"
|
qt_parse_all_arguments(arg "qt_add_module"
|
||||||
"NO_MODULE_HEADERS;STATIC;DISABLE_TOOLS_EXPORT;EXCEPTIONS;INTERNAL_MODULE;NO_SYNC_QT;NO_PRIVATE_MODULE;HEADER_MODULE;GENERATE_METATYPES;NO_CONFIG_HEADER_FILE;SKIP_DEPENDS_INCLUDE;NO_ADDITIONAL_TARGET_INFO"
|
"NO_MODULE_HEADERS;STATIC;DISABLE_TOOLS_EXPORT;EXCEPTIONS;INTERNAL_MODULE;NO_SYNC_QT;NO_PRIVATE_MODULE;HEADER_MODULE;GENERATE_METATYPES;NO_GENERATE_METATYPES;NO_CONFIG_HEADER_FILE;SKIP_DEPENDS_INCLUDE;NO_ADDITIONAL_TARGET_INFO"
|
||||||
"MODULE_INCLUDE_NAME;CONFIG_MODULE_NAME;PRECOMPILED_HEADER;CONFIGURE_FILE_PATH;${__default_target_info_args}"
|
"MODULE_INCLUDE_NAME;CONFIG_MODULE_NAME;PRECOMPILED_HEADER;CONFIGURE_FILE_PATH;${__default_target_info_args}"
|
||||||
"${__default_private_args};${__default_public_args};${__default_private_module_args};QMAKE_MODULE_CONFIG;EXTRA_CMAKE_FILES;EXTRA_CMAKE_INCLUDES;NO_PCH_SOURCES" ${ARGN})
|
"${__default_private_args};${__default_public_args};${__default_private_module_args};QMAKE_MODULE_CONFIG;EXTRA_CMAKE_FILES;EXTRA_CMAKE_INCLUDES;NO_PCH_SOURCES" ${ARGN})
|
||||||
|
|
||||||
@ -498,15 +499,24 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Generate metatypes
|
# Generate metatypes
|
||||||
if (${arg_GENERATE_METATYPES})
|
if(${arg_GENERATE_METATYPES})
|
||||||
set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes)
|
# No mention of NO_GENERATE_METATYPES. You should not use it.
|
||||||
set(args)
|
message(WARNING "GENERATE_METATYPES is on by default for Qt modules. Please remove the manual specification.")
|
||||||
if (NOT QT_WILL_INSTALL)
|
endif()
|
||||||
set(args COPY_OVER_INSTALL INSTALL_DIR "${QT_BUILD_DIR}/${metatypes_install_dir}")
|
if (NOT ${arg_NO_GENERATE_METATYPES})
|
||||||
else()
|
get_target_property(target_type ${target} TYPE)
|
||||||
set(args INSTALL_DIR "${metatypes_install_dir}")
|
if (NOT target_type STREQUAL "INTERFACE_LIBRARY")
|
||||||
|
set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes)
|
||||||
|
set(args)
|
||||||
|
if (NOT QT_WILL_INSTALL)
|
||||||
|
set(args COPY_OVER_INSTALL INSTALL_DIR "${QT_BUILD_DIR}/${metatypes_install_dir}")
|
||||||
|
else()
|
||||||
|
set(args INSTALL_DIR "${metatypes_install_dir}")
|
||||||
|
endif()
|
||||||
|
qt6_extract_metatypes(${target} ${args})
|
||||||
|
elseif(${arg_GENERATE_METATYPES})
|
||||||
|
message(FATAL_ERROR "Meta types generation does not work on interface libraries")
|
||||||
endif()
|
endif()
|
||||||
qt6_extract_metatypes(${target} ${args})
|
|
||||||
endif()
|
endif()
|
||||||
qt_internal_get_min_new_policy_cmake_version(min_new_policy_version)
|
qt_internal_get_min_new_policy_cmake_version(min_new_policy_version)
|
||||||
qt_internal_get_max_new_policy_cmake_version(max_new_policy_version)
|
qt_internal_get_max_new_policy_cmake_version(max_new_policy_version)
|
||||||
|
5
config.tests/cmake_zstd/check_zstd.cmake
Normal file
5
config.tests/cmake_zstd/check_zstd.cmake
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
file(ARCHIVE_CREATE
|
||||||
|
OUTPUT cmake_zstd.zstd
|
||||||
|
PATHS "${CMAKE_CURRENT_LIST_FILE}"
|
||||||
|
FORMAT raw
|
||||||
|
COMPRESSION Zstd)
|
@ -830,6 +830,24 @@ qt_feature("zstd" PRIVATE
|
|||||||
LABEL "Zstandard support"
|
LABEL "Zstandard support"
|
||||||
CONDITION ZSTD_FOUND
|
CONDITION ZSTD_FOUND
|
||||||
)
|
)
|
||||||
|
# special case begin
|
||||||
|
# Check whether CMake was built with zstd support.
|
||||||
|
# See https://gitlab.kitware.com/cmake/cmake/-/issues/21552
|
||||||
|
if(NOT DEFINED CACHE{QT_CMAKE_ZSTD_SUPPORT})
|
||||||
|
set(QT_CMAKE_ZSTD_SUPPORT FALSE CACHE INTERNAL "")
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18")
|
||||||
|
execute_process(COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/config.tests/cmake_zstd/check_zstd.cmake"
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/config.tests"
|
||||||
|
OUTPUT_QUIET ERROR_QUIET
|
||||||
|
RESULT_VARIABLE qt_check_zstd_exit_code)
|
||||||
|
if(qt_check_zstd_exit_code EQUAL 0)
|
||||||
|
set(QT_CMAKE_ZSTD_SUPPORT TRUE CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
unset(qt_check_zstd_exit_code)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
# special case end
|
||||||
qt_feature("thread" PUBLIC
|
qt_feature("thread" PUBLIC
|
||||||
SECTION "Kernel"
|
SECTION "Kernel"
|
||||||
LABEL "Thread support"
|
LABEL "Thread support"
|
||||||
|
@ -214,7 +214,7 @@ void SettingsTree::updateChildItems(QTreeWidgetItem *parent)
|
|||||||
if (value.userType() == QMetaType::UnknownType) {
|
if (value.userType() == QMetaType::UnknownType) {
|
||||||
child->setText(1, "Invalid");
|
child->setText(1, "Invalid");
|
||||||
} else {
|
} else {
|
||||||
if (value.type() == QVariant::String) {
|
if (value.typeId() == QMetaType::QString) {
|
||||||
const QString stringValue = value.toString();
|
const QString stringValue = value.toString();
|
||||||
if (m_typeChecker->boolExp.match(stringValue).hasMatch()) {
|
if (m_typeChecker->boolExp.match(stringValue).hasMatch()) {
|
||||||
value.setValue(stringValue.compare("true", Qt::CaseInsensitive) == 0);
|
value.setValue(stringValue.compare("true", Qt::CaseInsensitive) == 0);
|
||||||
|
@ -32,6 +32,7 @@ endif()
|
|||||||
|
|
||||||
qt_internal_add_module(Core
|
qt_internal_add_module(Core
|
||||||
QMAKE_MODULE_CONFIG moc resources
|
QMAKE_MODULE_CONFIG moc resources
|
||||||
|
NO_GENERATE_METATYPES # metatypes are extracted manually below
|
||||||
EXCEPTIONS
|
EXCEPTIONS
|
||||||
SOURCES
|
SOURCES
|
||||||
global/archdetect.cpp
|
global/archdetect.cpp
|
||||||
@ -1186,6 +1187,11 @@ if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
|
|||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
if(QT_FEATURE_zstd)
|
if(QT_FEATURE_zstd)
|
||||||
|
if(NOT QT_CMAKE_ZSTD_SUPPORT)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"CMake was not built with zstd support. "
|
||||||
|
"Rebuild CMake or set QT_AVOID_CMAKE_ARCHIVING_API=ON.")
|
||||||
|
endif()
|
||||||
set(qmime_db_compression Zstd)
|
set(qmime_db_compression Zstd)
|
||||||
string(APPEND qmime_db_content "#define MIME_DATABASE_IS_ZSTD\n")
|
string(APPEND qmime_db_content "#define MIME_DATABASE_IS_ZSTD\n")
|
||||||
else()
|
else()
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
\externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
\externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
||||||
\title Oracle: JNI Functions
|
\title Java: JNI Functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -90,3 +90,13 @@
|
|||||||
\externalpage https://developer.android.com/training/articles/perf-jni#local-and-global-references
|
\externalpage https://developer.android.com/training/articles/perf-jni#local-and-global-references
|
||||||
\title JNI tips: Local and global references
|
\title JNI tips: Local and global references
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread
|
||||||
|
\title Java: AttachCurrentThread
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\externalpage https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html#interface-function-table
|
||||||
|
\title Java: Interface Function Table
|
||||||
|
*/
|
||||||
|
@ -50,8 +50,7 @@
|
|||||||
The binding expression computes the value by reading other QProperty values.
|
The binding expression computes the value by reading other QProperty values.
|
||||||
Behind the scenes this dependency is tracked. Whenever a change in any property's
|
Behind the scenes this dependency is tracked. Whenever a change in any property's
|
||||||
dependency is detected, the binding expression is re-evaluated and the new
|
dependency is detected, the binding expression is re-evaluated and the new
|
||||||
result is applied to the property. This happens lazily, by marking the binding
|
result is applied to the property. For example:
|
||||||
as dirty and evaluating it only when the property's value is requested. For example:
|
|
||||||
|
|
||||||
\code
|
\code
|
||||||
QProperty<QString> firstname("John");
|
QProperty<QString> firstname("John");
|
||||||
@ -63,20 +62,19 @@
|
|||||||
|
|
||||||
qDebug() << fullname.value(); // Prints "John Smith age: 41"
|
qDebug() << fullname.value(); // Prints "John Smith age: 41"
|
||||||
|
|
||||||
firstname = "Emma"; // Marks binding expression as dirty
|
firstname = "Emma"; // Triggers binding reevaluation
|
||||||
|
|
||||||
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"
|
qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"
|
||||||
|
|
||||||
// Birthday is coming up
|
// Birthday is coming up
|
||||||
age.setValue(age.value() + 1);
|
age.setValue(age.value() + 1); // Triggers re-evaluation
|
||||||
|
|
||||||
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42"
|
qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
When a new value is assigned to the \c firstname property, the binding
|
When a new value is assigned to the \c firstname property, the binding
|
||||||
expression for \c fullname is marked as dirty. So when the last \c qDebug() statement
|
expression for \c fullname is reevaluated. So when the last \c qDebug() statement
|
||||||
tries to read the name value of the \c fullname property, the expression is
|
tries to read the name value of the \c fullname property, the new value is returned.
|
||||||
evaluated again, \c firstname() will be called again and return the new value.
|
|
||||||
|
|
||||||
Since bindings are C++ functions, they may do anything that's possible
|
Since bindings are C++ functions, they may do anything that's possible
|
||||||
in C++. This includes calling other functions. If those functions access values
|
in C++. This includes calling other functions. If those functions access values
|
||||||
|
@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE
|
|||||||
Since \c JNIEnv doesn't do much error checking, such as exception checking and clearing,
|
Since \c JNIEnv doesn't do much error checking, such as exception checking and clearing,
|
||||||
QJniEnvironment allows you to do that easily.
|
QJniEnvironment allows you to do that easily.
|
||||||
|
|
||||||
|
For more information about JNIEnv, see \l {Java: Interface Function Table}.
|
||||||
|
|
||||||
\note This API has been designed and tested for use with Android.
|
\note This API has been designed and tested for use with Android.
|
||||||
It has not been tested for other platforms.
|
It has not been tested for other platforms.
|
||||||
*/
|
*/
|
||||||
@ -266,7 +268,7 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
|
|||||||
In contrast to \l QJniObject, which handles exceptions internally, if you
|
In contrast to \l QJniObject, which handles exceptions internally, if you
|
||||||
make JNI calls directly via \c JNIEnv, you need to clear any potential
|
make JNI calls directly via \c JNIEnv, you need to clear any potential
|
||||||
exceptions after the call using this function. For more information about
|
exceptions after the call using this function. For more information about
|
||||||
\c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
|
\c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
|
||||||
|
|
||||||
\return \c true when a pending exception was cleared.
|
\return \c true when a pending exception was cleared.
|
||||||
*/
|
*/
|
||||||
@ -293,7 +295,7 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output
|
|||||||
In contrast to \l QJniObject, which handles exceptions internally, if you
|
In contrast to \l QJniObject, which handles exceptions internally, if you
|
||||||
make JNI calls directly via \c JNIEnv, you need to clear any potential
|
make JNI calls directly via \c JNIEnv, you need to clear any potential
|
||||||
exceptions after the call using this function. For more information about
|
exceptions after the call using this function. For more information about
|
||||||
\c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
|
\c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
|
||||||
|
|
||||||
\return \c true when a pending exception was cleared.
|
\return \c true when a pending exception was cleared.
|
||||||
*/
|
*/
|
||||||
|
@ -160,7 +160,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
\l {JNI Design Overview: Global and Local References}. Local references
|
\l {JNI Design Overview: Global and Local References}. Local references
|
||||||
created outside a native method scope must be deleted manually, since
|
created outside a native method scope must be deleted manually, since
|
||||||
the garbage collector will not free them automatically because we are using
|
the garbage collector will not free them automatically because we are using
|
||||||
\c AttachCurrentThread. For more information, see
|
\l {Java: AttachCurrentThread}{AttachCurrentThread}. For more information, see
|
||||||
\l {JNI tips: Local and global references}.
|
\l {JNI tips: Local and global references}.
|
||||||
|
|
||||||
If you want to keep a Java object alive you need to either create a new global
|
If you want to keep a Java object alive you need to either create a new global
|
||||||
|
@ -66,16 +66,17 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
|
|||||||
|
|
||||||
void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
|
void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
|
||||||
{
|
{
|
||||||
if (auto *binding = bindingPtr()) {
|
if (auto *b = binding()) {
|
||||||
observer->prev = &binding->firstObserver.ptr;
|
observer->prev = &b->firstObserver.ptr;
|
||||||
observer->next = binding->firstObserver.ptr;
|
observer->next = b->firstObserver.ptr;
|
||||||
if (observer->next)
|
if (observer->next)
|
||||||
observer->next->prev = &observer->next;
|
observer->next->prev = &observer->next;
|
||||||
binding->firstObserver.ptr = observer;
|
b->firstObserver.ptr = observer;
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(!(ptr->d_ptr & QPropertyBindingData::BindingBit));
|
auto &d = ptr->d_ref();
|
||||||
auto firstObserver = reinterpret_cast<QPropertyObserver*>(ptr->d_ptr);
|
Q_ASSERT(!(d & QPropertyBindingData::BindingBit));
|
||||||
observer->prev = reinterpret_cast<QPropertyObserver**>(&ptr->d_ptr);
|
auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
|
||||||
|
observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
|
||||||
observer->next = firstObserver;
|
observer->next = firstObserver;
|
||||||
if (observer->next)
|
if (observer->next)
|
||||||
observer->next->prev = &observer->next;
|
observer->next->prev = &observer->next;
|
||||||
@ -83,6 +84,182 @@ void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
|
|||||||
setFirstObserver(observer);
|
setFirstObserver(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
|
||||||
|
QPropertyDelayedNotifications is used to manage delayed notifications in grouped property updates.
|
||||||
|
It acts as a pool allocator for QPropertyProxyBindingData, and has methods to manage delayed
|
||||||
|
notifications.
|
||||||
|
|
||||||
|
\sa beginPropertyUpdateGroup, endPropertyUpdateGroup
|
||||||
|
*/
|
||||||
|
struct QPropertyDelayedNotifications
|
||||||
|
{
|
||||||
|
// we can't access the dynamic page size as we need a constant value
|
||||||
|
// use 4096 as a sensible default
|
||||||
|
static constexpr inline auto PageSize = 4096;
|
||||||
|
int ref = 0;
|
||||||
|
QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
|
||||||
|
qsizetype used = 0;
|
||||||
|
// Size chosen to avoid allocating more than one page of memory, while still ensuring
|
||||||
|
// that we can store many delayed properties without doing further allocations
|
||||||
|
static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
|
||||||
|
QPropertyProxyBindingData delayedProperties[size];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
This method is called when a property attempts to notify its observers while inside of a
|
||||||
|
property update group. Instead of actually notifying, it replaces \a bindingData's d_ptr
|
||||||
|
with a QPropertyProxyBindingData.
|
||||||
|
\a bindingData and \a propertyData are the binding data and property data of the property
|
||||||
|
whose notify call gets delayed.
|
||||||
|
\sa QPropertyBindingData::notifyObservers
|
||||||
|
*/
|
||||||
|
void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
|
||||||
|
if (bindingData->isNotificationDelayed())
|
||||||
|
return;
|
||||||
|
auto *data = this;
|
||||||
|
while (data->used == size) {
|
||||||
|
if (!data->next)
|
||||||
|
// add a new page
|
||||||
|
data->next = new QPropertyDelayedNotifications;
|
||||||
|
data = data->next;
|
||||||
|
}
|
||||||
|
auto *delayed = data->delayedProperties + data->used;
|
||||||
|
*delayed = QPropertyProxyBindingData { bindingData->d_ptr, bindingData, propertyData };
|
||||||
|
++data->used;
|
||||||
|
// preserve the binding bit for faster access
|
||||||
|
quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
|
||||||
|
bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
|
||||||
|
Q_ASSERT(bindingData->d_ptr > 3);
|
||||||
|
if (!bindingBit) {
|
||||||
|
if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
|
||||||
|
observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
|
||||||
|
\a index, it
|
||||||
|
\list
|
||||||
|
\li restores the original binding data that was modified in addProperty and
|
||||||
|
\li evaluates any bindings which depend on properties that were changed inside
|
||||||
|
the group.
|
||||||
|
\endlist
|
||||||
|
Change notifications are sent later with notify (following the logic of separating
|
||||||
|
binding updates and notifications used in non-deferred updates).
|
||||||
|
*/
|
||||||
|
void evaluateBindings(int index) {
|
||||||
|
auto *delayed = delayedProperties + index;
|
||||||
|
auto *bindingData = delayed->originalBindingData;
|
||||||
|
if (!bindingData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bindingData->d_ptr = delayed->d_ptr;
|
||||||
|
Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
|
||||||
|
if (!bindingData->hasBinding()) {
|
||||||
|
if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
|
||||||
|
observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPropertyBindingDataPointer bindingDataPointer{bindingData};
|
||||||
|
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
|
||||||
|
if (observer)
|
||||||
|
observer.evaluateBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
|
||||||
|
\a i, it
|
||||||
|
\list
|
||||||
|
\li resets the proxy binding data and
|
||||||
|
\li sends any pending notifications.
|
||||||
|
\endlist
|
||||||
|
*/
|
||||||
|
void notify(int index) {
|
||||||
|
auto *delayed = delayedProperties + index;
|
||||||
|
auto *bindingData = delayed->originalBindingData;
|
||||||
|
if (!bindingData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delayed->originalBindingData = nullptr;
|
||||||
|
delayed->d_ptr = 0;
|
||||||
|
|
||||||
|
QPropertyBindingDataPointer bindingDataPointer{bindingData};
|
||||||
|
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
|
||||||
|
if (observer)
|
||||||
|
observer.notify(delayed->propertyData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static thread_local QPropertyDelayedNotifications *groupUpdateData = nullptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.2
|
||||||
|
|
||||||
|
\relates template<typename T> QProperty<T>
|
||||||
|
|
||||||
|
Marks the beginning of a property update group. Inside this group,
|
||||||
|
changing a property does neither immediately update any dependent properties
|
||||||
|
nor does it trigger change notifications.
|
||||||
|
Those are instead deferred until the group is ended by a call to endPropertyUpdateGroup.
|
||||||
|
|
||||||
|
Groups can be nested. In that case, the deferral ends only after the outermost group has been
|
||||||
|
ended.
|
||||||
|
|
||||||
|
\note Change notifications are only send after all property values affected by the group have
|
||||||
|
been updated to their new values. This allows re-establishing a class invariant if multiple
|
||||||
|
properties need to be updated, preventing any external observer from noticing an inconsistent
|
||||||
|
state.
|
||||||
|
|
||||||
|
\sa Qt::endPropertyUpdateGroup
|
||||||
|
*/
|
||||||
|
void Qt::beginPropertyUpdateGroup()
|
||||||
|
{
|
||||||
|
if (!groupUpdateData)
|
||||||
|
groupUpdateData = new QPropertyDelayedNotifications;
|
||||||
|
++groupUpdateData->ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.2
|
||||||
|
\relates template<typename T> QProperty<T>
|
||||||
|
|
||||||
|
Ends a property update group. If the outermost group has been ended, and deferred
|
||||||
|
binding evaluations and notifications happen now.
|
||||||
|
|
||||||
|
\warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
|
||||||
|
results in undefined behavior.
|
||||||
|
|
||||||
|
\sa Qt::beginPropertyUpdateGroup
|
||||||
|
*/
|
||||||
|
void Qt::endPropertyUpdateGroup()
|
||||||
|
{
|
||||||
|
auto *data = groupUpdateData;
|
||||||
|
Q_ASSERT(data->ref);
|
||||||
|
if (--data->ref)
|
||||||
|
return;
|
||||||
|
groupUpdateData = nullptr;
|
||||||
|
// update all delayed properties
|
||||||
|
auto start = data;
|
||||||
|
while (data) {
|
||||||
|
for (int i = 0; i < data->used; ++i)
|
||||||
|
data->evaluateBindings(i);
|
||||||
|
data = data->next;
|
||||||
|
}
|
||||||
|
// notify all delayed properties
|
||||||
|
data = start;
|
||||||
|
while (data) {
|
||||||
|
for (int i = 0; i < data->used; ++i)
|
||||||
|
data->notify(i);
|
||||||
|
auto *next = data->next;
|
||||||
|
delete data;
|
||||||
|
data = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QPropertyBindingPrivate::~QPropertyBindingPrivate()
|
QPropertyBindingPrivate::~QPropertyBindingPrivate()
|
||||||
{
|
{
|
||||||
if (firstObserver)
|
if (firstObserver)
|
||||||
@ -98,44 +275,17 @@ void QPropertyBindingPrivate::unlinkAndDeref()
|
|||||||
destroyAndFreeMemory(this);
|
destroyAndFreeMemory(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
|
void QPropertyBindingPrivate::evaluateRecursive()
|
||||||
{
|
{
|
||||||
if (eagerlyUpdating) {
|
|
||||||
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
|
||||||
if (isQQmlPropertyBinding)
|
|
||||||
errorCallBack(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dirty)
|
|
||||||
return;
|
|
||||||
dirty = true;
|
|
||||||
|
|
||||||
eagerlyUpdating = true;
|
|
||||||
QScopeGuard guard([&](){eagerlyUpdating = false;});
|
|
||||||
bool knownToHaveChanged = false;
|
|
||||||
if (requiresEagerEvaluation()) {
|
|
||||||
// these are compat properties that we will need to evaluate eagerly
|
|
||||||
if (!evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
|
|
||||||
return;
|
|
||||||
knownToHaveChanged = true;
|
|
||||||
}
|
|
||||||
if (firstObserver)
|
|
||||||
firstObserver.notify(this, propertyDataPtr, knownToHaveChanged);
|
|
||||||
if (hasStaticObserver)
|
|
||||||
staticObserverCallback(propertyDataPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status)
|
|
||||||
{
|
|
||||||
Q_ASSERT(dirty);
|
|
||||||
|
|
||||||
if (updating) {
|
if (updating) {
|
||||||
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
|
||||||
if (isQQmlPropertyBinding)
|
if (isQQmlPropertyBinding)
|
||||||
errorCallBack(this);
|
errorCallBack(this);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScopedValueRollback<bool> updateGuard(updating, true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluating the binding might lead to the binding being broken. This can
|
* Evaluating the binding might lead to the binding being broken. This can
|
||||||
* cause ref to reach zero at the end of the function. However, the
|
* cause ref to reach zero at the end of the function. However, the
|
||||||
@ -145,23 +295,42 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(
|
|||||||
* that the object is still alive when updateGuard's dtor runs.
|
* that the object is still alive when updateGuard's dtor runs.
|
||||||
*/
|
*/
|
||||||
QPropertyBindingPrivatePtr keepAlive {this};
|
QPropertyBindingPrivatePtr keepAlive {this};
|
||||||
QScopedValueRollback<bool> updateGuard(updating, true);
|
|
||||||
|
|
||||||
BindingEvaluationState evaluationFrame(this, status);
|
BindingEvaluationState evaluationFrame(this);
|
||||||
|
|
||||||
|
auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
|
||||||
|
QPropertyBindingPrivate::getSizeEnsuringAlignment();
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
Q_ASSERT(propertyDataPtr == data);
|
|
||||||
QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data);
|
|
||||||
|
|
||||||
if (hasBindingWrapper) {
|
if (hasBindingWrapper) {
|
||||||
changed = staticBindingWrapper(metaType, mutable_data, {vtable, reinterpret_cast<std::byte *>(this)+QPropertyBindingPrivate::getSizeEnsuringAlignment()});
|
changed = staticBindingWrapper(metaType, propertyDataPtr,
|
||||||
|
{vtable, bindingFunctor});
|
||||||
} else {
|
} else {
|
||||||
changed = vtable->call(metaType, mutable_data, reinterpret_cast<std::byte *>(this)+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
|
changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
|
||||||
}
|
}
|
||||||
|
// If there was a change, we must set pendingNotify.
|
||||||
|
// If there was not, we must not clear it, as that only should happen in notifyRecursive
|
||||||
|
pendingNotify = pendingNotify || changed;
|
||||||
|
if (!changed || !firstObserver)
|
||||||
|
return;
|
||||||
|
|
||||||
dirty = false;
|
firstObserver.noSelfDependencies(this);
|
||||||
return changed;
|
firstObserver.evaluateBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QPropertyBindingPrivate::notifyRecursive()
|
||||||
|
{
|
||||||
|
if (!pendingNotify)
|
||||||
|
return;
|
||||||
|
pendingNotify = false;
|
||||||
|
Q_ASSERT(!updating);
|
||||||
|
updating = true;
|
||||||
|
if (firstObserver) {
|
||||||
|
firstObserver.noSelfDependencies(this);
|
||||||
|
firstObserver.notify(propertyDataPtr);
|
||||||
|
}
|
||||||
|
if (hasStaticObserver)
|
||||||
|
staticObserverCallback(propertyDataPtr);
|
||||||
|
updating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
|
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
|
||||||
@ -232,7 +401,7 @@ QPropertyBindingData::~QPropertyBindingData()
|
|||||||
observer.unlink();
|
observer.unlink();
|
||||||
observer = next;
|
observer = next;
|
||||||
}
|
}
|
||||||
if (auto binding = d.bindingPtr())
|
if (auto binding = d.binding())
|
||||||
binding->unlinkAndDeref();
|
binding->unlinkAndDeref();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,42 +416,38 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
|||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
QPropertyObserverPointer observer;
|
QPropertyObserverPointer observer;
|
||||||
|
|
||||||
if (auto *existingBinding = d.bindingPtr()) {
|
auto &data = d_ref();
|
||||||
|
if (auto *existingBinding = d.binding()) {
|
||||||
if (existingBinding == newBinding.data())
|
if (existingBinding == newBinding.data())
|
||||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||||
if (existingBinding->isEagerlyUpdating()) {
|
if (existingBinding->isUpdating()) {
|
||||||
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
|
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
|
||||||
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
|
||||||
}
|
}
|
||||||
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
|
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
|
||||||
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
|
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
|
||||||
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
|
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
|
||||||
d_ptr = 0;
|
data = 0;
|
||||||
} else {
|
} else {
|
||||||
observer = d.firstObserver();
|
observer = d.firstObserver();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newBinding) {
|
if (newBinding) {
|
||||||
newBinding.data()->addRef();
|
newBinding.data()->addRef();
|
||||||
d_ptr = reinterpret_cast<quintptr>(newBinding.data());
|
data = reinterpret_cast<quintptr>(newBinding.data());
|
||||||
d_ptr |= BindingBit;
|
data |= BindingBit;
|
||||||
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
|
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
|
||||||
newBindingRaw->setDirty(true);
|
|
||||||
newBindingRaw->setProperty(propertyDataPtr);
|
newBindingRaw->setProperty(propertyDataPtr);
|
||||||
if (observer)
|
if (observer)
|
||||||
newBindingRaw->prependObserver(observer);
|
newBindingRaw->prependObserver(observer);
|
||||||
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
|
||||||
if (newBindingRaw->requiresEagerEvaluation()) {
|
|
||||||
newBindingRaw->setEagerlyUpdating(true);
|
newBindingRaw->evaluateRecursive();
|
||||||
auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
|
newBindingRaw->notifyRecursive();
|
||||||
if (changed)
|
|
||||||
observer.notify(newBindingRaw, propertyDataPtr, /*knownToHaveChanged=*/true);
|
|
||||||
newBindingRaw->setEagerlyUpdating(false);
|
|
||||||
}
|
|
||||||
} else if (observer) {
|
} else if (observer) {
|
||||||
d.setObservers(observer.ptr);
|
d.setObservers(observer.ptr);
|
||||||
} else {
|
} else {
|
||||||
d_ptr &= ~QPropertyBindingData::BindingBit;
|
data = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldBinding)
|
if (oldBinding)
|
||||||
@ -293,8 +458,7 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
|
|||||||
|
|
||||||
QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0))
|
QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0))
|
||||||
{
|
{
|
||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer::fixupAfterMove(this);
|
||||||
d.fixupFirstObserverAfterMove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static thread_local QBindingStatus bindingStatus;
|
static thread_local QBindingStatus bindingStatus;
|
||||||
@ -333,24 +497,20 @@ QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
|
|||||||
return currentState ? currentState->binding : nullptr;
|
return currentState ? currentState->binding : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *property) const
|
// ### Unused, kept for BC with 6.0
|
||||||
|
void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
|
||||||
{
|
{
|
||||||
QPropertyBindingDataPointer d{this};
|
|
||||||
QPropertyBindingPrivate *binding = d.bindingPtr();
|
|
||||||
if (!binding)
|
|
||||||
return;
|
|
||||||
binding->evaluateIfDirtyAndReturnTrueIfValueChanged(property);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingData::removeBinding_helper()
|
void QPropertyBindingData::removeBinding_helper()
|
||||||
{
|
{
|
||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
|
|
||||||
auto *existingBinding = d.bindingPtr();
|
auto *existingBinding = d.binding();
|
||||||
Q_ASSERT(existingBinding);
|
Q_ASSERT(existingBinding);
|
||||||
|
|
||||||
auto observer = existingBinding->takeObservers();
|
auto observer = existingBinding->takeObservers();
|
||||||
d_ptr = 0;
|
d_ref() = 0;
|
||||||
if (observer)
|
if (observer)
|
||||||
d.setObservers(observer.ptr);
|
d.setObservers(observer.ptr);
|
||||||
existingBinding->unlinkAndDeref();
|
existingBinding->unlinkAndDeref();
|
||||||
@ -370,22 +530,25 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
|
|||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
|
|
||||||
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
|
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
|
||||||
dependencyObserver.setBindingToMarkDirty(currentState->binding);
|
dependencyObserver.setBindingToNotify(currentState->binding);
|
||||||
dependencyObserver.observeProperty(d);
|
dependencyObserver.observeProperty(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
|
void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
|
||||||
{
|
{
|
||||||
|
if (isNotificationDelayed())
|
||||||
|
return;
|
||||||
QPropertyBindingDataPointer d{this};
|
QPropertyBindingDataPointer d{this};
|
||||||
if (QPropertyObserverPointer observer = d.firstObserver())
|
QPropertyObserverPointer observer = d.firstObserver();
|
||||||
observer.notify(d.bindingPtr(), propertyDataPtr);
|
if (!observer)
|
||||||
}
|
return;
|
||||||
|
auto *delay = groupUpdateData;
|
||||||
void QPropertyBindingData::markDirty()
|
if (delay) {
|
||||||
{
|
delay->addProperty(this, propertyDataPtr);
|
||||||
QPropertyBindingDataPointer d{this};
|
return;
|
||||||
if (auto *binding = d.bindingPtr())
|
}
|
||||||
binding->setDirty(true);
|
observer.evaluateBindings();
|
||||||
|
observer.notify(propertyDataPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int QPropertyBindingDataPointer::observerCount() const
|
int QPropertyBindingDataPointer::observerCount() const
|
||||||
@ -425,7 +588,7 @@ QPropertyObserver::~QPropertyObserver()
|
|||||||
|
|
||||||
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
|
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
|
||||||
{
|
{
|
||||||
bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
|
binding = std::exchange(other.binding, {});
|
||||||
next = std::exchange(other.next, {});
|
next = std::exchange(other.next, {});
|
||||||
prev = std::exchange(other.prev, {});
|
prev = std::exchange(other.prev, {});
|
||||||
if (next)
|
if (next)
|
||||||
@ -441,9 +604,9 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
|
|||||||
|
|
||||||
QPropertyObserverPointer d{this};
|
QPropertyObserverPointer d{this};
|
||||||
d.unlink();
|
d.unlink();
|
||||||
bindingToMarkDirty = nullptr;
|
binding = nullptr;
|
||||||
|
|
||||||
bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
|
binding = std::exchange(other.binding, {});
|
||||||
next = std::exchange(other.next, {});
|
next = std::exchange(other.next, {});
|
||||||
prev = std::exchange(other.prev, {});
|
prev = std::exchange(other.prev, {});
|
||||||
if (next)
|
if (next)
|
||||||
@ -480,10 +643,10 @@ void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property
|
|||||||
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
|
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding)
|
void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
|
||||||
{
|
{
|
||||||
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
|
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
|
||||||
ptr->bindingToMarkDirty = binding;
|
ptr->binding = binding;
|
||||||
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
|
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,14 +679,8 @@ struct [[nodiscard]] QPropertyObserverNodeProtector {
|
|||||||
|
|
||||||
/*! \internal
|
/*! \internal
|
||||||
\a propertyDataPtr is a pointer to the observed property's property data
|
\a propertyDataPtr is a pointer to the observed property's property data
|
||||||
In case that property has a binding, \a triggeringBinding points to the binding's QPropertyBindingPrivate
|
|
||||||
\a alreadyKnownToHaveChanged is an optional parameter, which is needed in the case
|
|
||||||
of eager evaluation:
|
|
||||||
There, we have already evaluated the binding, and thus the change detection for the
|
|
||||||
ObserverNotifiesChangeHandler case would not work. Thus we instead pass the knowledge of
|
|
||||||
whether the value has changed we obtained when evaluating the binding eagerly along
|
|
||||||
*/
|
*/
|
||||||
void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged)
|
void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
|
||||||
{
|
{
|
||||||
auto observer = const_cast<QPropertyObserver*>(ptr);
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
/*
|
/*
|
||||||
@ -557,22 +714,17 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
|
|||||||
observer = next->next.data();
|
observer = next->next.data();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list
|
// handlerToCall might modify the list
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
if (!knownToHaveChanged && triggeringBinding) {
|
|
||||||
if (!triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
|
|
||||||
return;
|
|
||||||
knownToHaveChanged = true;
|
|
||||||
}
|
|
||||||
handlerToCall(observer, propertyDataPtr);
|
handlerToCall(observer, propertyDataPtr);
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QPropertyObserver::ObserverNotifiesBinding:
|
case QPropertyObserver::ObserverNotifiesBinding:
|
||||||
{
|
{
|
||||||
auto bindingToMarkDirty = observer->bindingToMarkDirty;
|
auto bindingToNotify = observer->binding;
|
||||||
QPropertyObserverNodeProtector protector(observer);
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
bindingToMarkDirty->markDirtyAndNotifyObservers();
|
bindingToNotify->notifyRecursive();
|
||||||
next = protector.next();
|
next = protector.next();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -586,6 +738,39 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
|
||||||
|
{
|
||||||
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
|
// See also comment in notify()
|
||||||
|
while (observer) {
|
||||||
|
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding)
|
||||||
|
Q_ASSERT(observer->binding != binding);
|
||||||
|
|
||||||
|
observer = observer->next.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void QPropertyObserverPointer::evaluateBindings()
|
||||||
|
{
|
||||||
|
auto observer = const_cast<QPropertyObserver*>(ptr);
|
||||||
|
// See also comment in notify()
|
||||||
|
while (observer) {
|
||||||
|
QPropertyObserver *next = observer->next.data();
|
||||||
|
|
||||||
|
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
|
||||||
|
auto bindingToEvaluate = observer->binding;
|
||||||
|
QPropertyObserverNodeProtector protector(observer);
|
||||||
|
bindingToEvaluate->evaluateRecursive();
|
||||||
|
next = protector.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
observer = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
|
void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
|
||||||
{
|
{
|
||||||
if (ptr->prev)
|
if (ptr->prev)
|
||||||
@ -1873,18 +2058,26 @@ QBindingStorage::~QBindingStorage()
|
|||||||
QBindingStoragePrivate(d).destroy();
|
QBindingStoragePrivate(d).destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ### Unused, retained for BC with 6.0
|
||||||
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
|
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
|
||||||
|
{
|
||||||
|
registerDependency_helper(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(bindingStatus);
|
Q_ASSERT(bindingStatus);
|
||||||
|
// Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
|
||||||
|
// another thread do not register as dependencies
|
||||||
|
auto *currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
|
||||||
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
|
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
|
||||||
auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr);
|
auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ currentBinding != nullptr);
|
||||||
if (!storage)
|
if (!storage)
|
||||||
return;
|
return;
|
||||||
if (auto *binding = storage->binding())
|
storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
|
||||||
binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data), bindingStatus);
|
|
||||||
storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
|
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
return QBindingStoragePrivate(d).get(data);
|
return QBindingStoragePrivate(d).get(data);
|
||||||
@ -1921,6 +2114,13 @@ bool isAnyBindingEvaluating()
|
|||||||
{
|
{
|
||||||
return bindingStatus.currentlyEvaluatingBinding != nullptr;
|
return bindingStatus.currentlyEvaluatingBinding != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
|
||||||
|
{
|
||||||
|
return bindingStatus.currentCompatProperty &&
|
||||||
|
bindingStatus.currentCompatProperty->property == property;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtPrivate end
|
} // namespace QtPrivate end
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -63,6 +63,11 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace Qt {
|
||||||
|
Q_CORE_EXPORT void beginPropertyUpdateGroup();
|
||||||
|
Q_CORE_EXPORT void endPropertyUpdateGroup();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class QPropertyData : public QUntypedPropertyData
|
class QPropertyData : public QUntypedPropertyData
|
||||||
{
|
{
|
||||||
@ -217,6 +222,7 @@ protected:
|
|||||||
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
|
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct QPropertyDelayedNotifications;
|
||||||
friend struct QPropertyObserverNodeProtector;
|
friend struct QPropertyObserverNodeProtector;
|
||||||
friend class QPropertyObserver;
|
friend class QPropertyObserver;
|
||||||
friend struct QPropertyObserverPointer;
|
friend struct QPropertyObserverPointer;
|
||||||
@ -229,7 +235,7 @@ private:
|
|||||||
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
|
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
|
QPropertyBindingPrivate *binding = nullptr;
|
||||||
ChangeHandler changeHandler;
|
ChangeHandler changeHandler;
|
||||||
QUntypedPropertyData *aliasedPropertyData;
|
QUntypedPropertyData *aliasedPropertyData;
|
||||||
};
|
};
|
||||||
@ -329,8 +335,6 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
if (d.hasBinding())
|
|
||||||
d.evaluateIfDirty(this);
|
|
||||||
d.registerWithCurrentlyEvaluatingBinding();
|
d.registerWithCurrentlyEvaluatingBinding();
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
@ -389,9 +393,7 @@ public:
|
|||||||
|
|
||||||
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
|
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
|
||||||
{
|
{
|
||||||
QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this));
|
return QPropertyBinding<T>(d.setBinding(newBinding, this));
|
||||||
notify();
|
|
||||||
return oldBinding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setBinding(const QUntypedPropertyBinding &newBinding)
|
bool setBinding(const QUntypedPropertyBinding &newBinding)
|
||||||
@ -402,11 +404,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
d.markDirty();
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef Q_CLANG_QDOC
|
#ifndef Q_CLANG_QDOC
|
||||||
template <typename Functor>
|
template <typename Functor>
|
||||||
QPropertyBinding<T> setBinding(Functor &&f,
|
QPropertyBinding<T> setBinding(Functor &&f,
|
||||||
@ -852,11 +849,11 @@ public:
|
|||||||
|
|
||||||
bool isEmpty() { return !d; }
|
bool isEmpty() { return !d; }
|
||||||
|
|
||||||
void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const
|
void registerDependency(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
if (!d && !bindingStatus->currentlyEvaluatingBinding)
|
if (!bindingStatus->currentlyEvaluatingBinding)
|
||||||
return;
|
return;
|
||||||
maybeUpdateBindingAndRegister_helper(data);
|
registerDependency_helper(data);
|
||||||
}
|
}
|
||||||
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
|
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
|
||||||
{
|
{
|
||||||
@ -864,6 +861,9 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
return bindingData_helper(data);
|
return bindingData_helper(data);
|
||||||
}
|
}
|
||||||
|
// ### Qt 7: remove unused BIC shim
|
||||||
|
void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
|
||||||
|
|
||||||
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
|
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
|
||||||
{
|
{
|
||||||
if (!d && !create)
|
if (!d && !create)
|
||||||
@ -871,6 +871,8 @@ public:
|
|||||||
return bindingData_helper(data, create);
|
return bindingData_helper(data, create);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
void registerDependency_helper(const QUntypedPropertyData *data) const;
|
||||||
|
// ### Unused, but keep for BC
|
||||||
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
|
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
|
||||||
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
|
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
|
||||||
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
|
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
|
||||||
@ -923,7 +925,7 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
|
qGetBindingStorage(owner())->registerDependency(this);
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -987,7 +989,6 @@ public:
|
|||||||
{
|
{
|
||||||
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
|
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
|
||||||
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
|
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
|
||||||
notify(bd);
|
|
||||||
return static_cast<QPropertyBinding<T> &>(oldBinding);
|
return static_cast<QPropertyBinding<T> &>(oldBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,15 +1019,6 @@ public:
|
|||||||
return bd && bd->binding() != nullptr;
|
return bd && bd->binding() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
|
||||||
auto bd = storage->bindingData(this, /*create=*/false);
|
|
||||||
if (bd) { // if we have no BindingData, nobody can listen anyway
|
|
||||||
bd->markDirty();
|
|
||||||
notify(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPropertyBinding<T> binding() const
|
QPropertyBinding<T> binding() const
|
||||||
{
|
{
|
||||||
auto *bd = qGetBindingStorage(owner())->bindingData(this);
|
auto *bd = qGetBindingStorage(owner())->bindingData(this);
|
||||||
@ -1130,7 +1122,7 @@ public:
|
|||||||
|
|
||||||
parameter_type value() const
|
parameter_type value() const
|
||||||
{
|
{
|
||||||
qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
|
qGetBindingStorage(owner())->registerDependency(this);
|
||||||
return (owner()->*Getter)();
|
return (owner()->*Getter)();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,15 +1168,13 @@ public:
|
|||||||
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
|
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
void notify() {
|
||||||
// computed property can't store a binding, so there's nothing to mark
|
// computed property can't store a binding, so there's nothing to mark
|
||||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||||
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
|
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
|
||||||
if (bd)
|
if (bd)
|
||||||
bindingData().notifyObservers(this);
|
bd->notifyObservers(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
|
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
|
||||||
|
@ -71,19 +71,18 @@ struct Q_AUTOTEST_EXPORT QPropertyBindingDataPointer
|
|||||||
{
|
{
|
||||||
const QtPrivate::QPropertyBindingData *ptr = nullptr;
|
const QtPrivate::QPropertyBindingData *ptr = nullptr;
|
||||||
|
|
||||||
QPropertyBindingPrivate *bindingPtr() const
|
QPropertyBindingPrivate *binding() const
|
||||||
{
|
{
|
||||||
if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
|
return ptr->binding();
|
||||||
return reinterpret_cast<QPropertyBindingPrivate*>(ptr->d_ptr - QtPrivate::QPropertyBindingData::BindingBit);
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setObservers(QPropertyObserver *observer)
|
void setObservers(QPropertyObserver *observer)
|
||||||
{
|
{
|
||||||
observer->prev = reinterpret_cast<QPropertyObserver**>(&(ptr->d_ptr));
|
auto &d = ptr->d_ref();
|
||||||
ptr->d_ptr = reinterpret_cast<quintptr>(observer);
|
observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
|
||||||
|
d = reinterpret_cast<quintptr>(observer);
|
||||||
}
|
}
|
||||||
void fixupFirstObserverAfterMove() const;
|
static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr);
|
||||||
void addObserver(QPropertyObserver *observer);
|
void addObserver(QPropertyObserver *observer);
|
||||||
void setFirstObserver(QPropertyObserver *observer);
|
void setFirstObserver(QPropertyObserver *observer);
|
||||||
QPropertyObserverPointer firstObserver() const;
|
QPropertyObserverPointer firstObserver() const;
|
||||||
@ -104,11 +103,17 @@ struct QPropertyObserverPointer
|
|||||||
|
|
||||||
void unlink();
|
void unlink();
|
||||||
|
|
||||||
void setBindingToMarkDirty(QPropertyBindingPrivate *binding);
|
void setBindingToNotify(QPropertyBindingPrivate *binding);
|
||||||
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
|
||||||
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
|
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
|
||||||
|
|
||||||
void notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged = false);
|
void notify(QUntypedPropertyData *propertyDataPtr);
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
void noSelfDependencies(QPropertyBindingPrivate *binding);
|
||||||
|
#else
|
||||||
|
void noSelfDependencies(QPropertyBindingPrivate *) {}
|
||||||
|
#endif
|
||||||
|
void evaluateBindings();
|
||||||
void observeProperty(QPropertyBindingDataPointer property);
|
void observeProperty(QPropertyBindingDataPointer property);
|
||||||
|
|
||||||
explicit operator bool() const { return ptr != nullptr; }
|
explicit operator bool() const { return ptr != nullptr; }
|
||||||
@ -172,14 +177,12 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// a dependent property has changed, and the binding needs to be reevaluated on access
|
|
||||||
bool dirty = false;
|
|
||||||
// used to detect binding loops for lazy evaluated properties
|
// used to detect binding loops for lazy evaluated properties
|
||||||
bool updating = false;
|
bool updating = false;
|
||||||
bool hasStaticObserver = false;
|
bool hasStaticObserver = false;
|
||||||
|
bool pendingNotify = false;
|
||||||
bool hasBindingWrapper:1;
|
bool hasBindingWrapper:1;
|
||||||
// used to detect binding loops for eagerly evaluated properties
|
// used to detect binding loops for eagerly evaluated properties
|
||||||
bool eagerlyUpdating:1;
|
|
||||||
bool isQQmlPropertyBinding:1;
|
bool isQQmlPropertyBinding:1;
|
||||||
|
|
||||||
const QtPrivate::BindingFunctionVTable *vtable;
|
const QtPrivate::BindingFunctionVTable *vtable;
|
||||||
@ -230,13 +233,11 @@ public:
|
|||||||
// public because the auto-tests access it, too.
|
// public because the auto-tests access it, too.
|
||||||
size_t dependencyObserverCount = 0;
|
size_t dependencyObserverCount = 0;
|
||||||
|
|
||||||
bool isEagerlyUpdating() {return eagerlyUpdating;}
|
bool isUpdating() {return updating;}
|
||||||
void setEagerlyUpdating(bool b) {eagerlyUpdating = b;}
|
|
||||||
|
|
||||||
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
|
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
|
||||||
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
|
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
|
||||||
: hasBindingWrapper(false)
|
: hasBindingWrapper(false)
|
||||||
, eagerlyUpdating(false)
|
|
||||||
, isQQmlPropertyBinding(isQQmlPropertyBinding)
|
, isQQmlPropertyBinding(isQQmlPropertyBinding)
|
||||||
, vtable(vtable)
|
, vtable(vtable)
|
||||||
, location(location)
|
, location(location)
|
||||||
@ -245,7 +246,6 @@ public:
|
|||||||
~QPropertyBindingPrivate();
|
~QPropertyBindingPrivate();
|
||||||
|
|
||||||
|
|
||||||
void setDirty(bool d) { dirty = d; }
|
|
||||||
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
|
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
|
||||||
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
|
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
|
||||||
{
|
{
|
||||||
@ -312,13 +312,8 @@ public:
|
|||||||
|
|
||||||
void unlinkAndDeref();
|
void unlinkAndDeref();
|
||||||
|
|
||||||
void markDirtyAndNotifyObservers();
|
void evaluateRecursive();
|
||||||
bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data, QBindingStatus *status = nullptr)
|
void notifyRecursive();
|
||||||
{
|
|
||||||
if (!dirty)
|
|
||||||
return false;
|
|
||||||
return evaluateIfDirtyAndReturnTrueIfValueChanged_helper(data, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
|
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
|
||||||
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
|
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
|
||||||
@ -334,8 +329,6 @@ public:
|
|||||||
clearDependencyObservers();
|
clearDependencyObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requiresEagerEvaluation() const { return hasBindingWrapper; }
|
|
||||||
|
|
||||||
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
|
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
|
||||||
|
|
||||||
bool hasCustomVTable() const
|
bool hasCustomVTable() const
|
||||||
@ -353,36 +346,44 @@ public:
|
|||||||
delete[] reinterpret_cast<std::byte *>(priv);
|
delete[] reinterpret_cast<std::byte *>(priv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
|
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
|
||||||
{
|
{
|
||||||
if (auto *binding = bindingPtr()) {
|
if (auto *b = binding()) {
|
||||||
binding->firstObserver.ptr = observer;
|
b->firstObserver.ptr = observer;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ptr->d_ptr = reinterpret_cast<quintptr>(observer);
|
auto &d = ptr->d_ref();
|
||||||
|
d = reinterpret_cast<quintptr>(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void QPropertyBindingDataPointer::fixupFirstObserverAfterMove() const
|
inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
|
||||||
{
|
{
|
||||||
|
auto &d = ptr->d_ref();
|
||||||
|
if (ptr->isNotificationDelayed()) {
|
||||||
|
QPropertyProxyBindingData *proxyData
|
||||||
|
= reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
|
||||||
|
proxyData->originalBindingData = ptr;
|
||||||
|
}
|
||||||
// If QPropertyBindingData has been moved, and it has an observer
|
// If QPropertyBindingData has been moved, and it has an observer
|
||||||
// we have to adjust the firstObesrver's prev pointer to point to
|
// we have to adjust the firstObserver's prev pointer to point to
|
||||||
// the moved to QPropertyBindingData's d_ptr
|
// the moved to QPropertyBindingData's d_ptr
|
||||||
if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
|
if (d & QtPrivate::QPropertyBindingData::BindingBit)
|
||||||
return; // nothing to do if the observer is stored in the binding
|
return; // nothing to do if the observer is stored in the binding
|
||||||
if (auto observer = firstObserver())
|
if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
|
||||||
observer.ptr->prev = reinterpret_cast<QPropertyObserver **>(&(ptr->d_ptr));
|
observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() const
|
inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() const
|
||||||
{
|
{
|
||||||
if (auto *binding = bindingPtr())
|
if (auto *b = binding())
|
||||||
return binding->firstObserver;
|
return b->firstObserver;
|
||||||
return { reinterpret_cast<QPropertyObserver *>(ptr->d_ptr) };
|
return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QtPrivate {
|
||||||
|
Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
|
template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
|
||||||
@ -414,10 +415,10 @@ class QObjectCompatProperty : public QPropertyData<T>
|
|||||||
(thisData->owner()->*Setter)(copy.valueBypassingBindings());
|
(thisData->owner()->*Setter)(copy.valueBypassingBindings());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
inline bool inBindingWrapper(const QBindingStorage *storage) const
|
bool inBindingWrapper(const QBindingStorage *storage) const
|
||||||
{
|
{
|
||||||
return storage->bindingStatus->currentCompatProperty &&
|
return storage->bindingStatus->currentCompatProperty
|
||||||
storage->bindingStatus->currentCompatProperty->property == this;
|
&& QtPrivate::isPropertyInBindingWrapper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -434,7 +435,7 @@ public:
|
|||||||
const QBindingStorage *storage = qGetBindingStorage(owner());
|
const QBindingStorage *storage = qGetBindingStorage(owner());
|
||||||
// make sure we don't register this binding as a dependency to itself
|
// make sure we don't register this binding as a dependency to itself
|
||||||
if (!inBindingWrapper(storage))
|
if (!inBindingWrapper(storage))
|
||||||
storage->maybeUpdateBindingAndRegister(this);
|
storage->registerDependency(this);
|
||||||
return this->val;
|
return this->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,15 +512,6 @@ public:
|
|||||||
return bd && bd->binding() != nullptr;
|
return bd && bd->binding() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markDirty() {
|
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
|
||||||
auto *bd = storage->bindingData(this, false);
|
|
||||||
if (bd) {
|
|
||||||
bd->markDirty();
|
|
||||||
notify(bd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeBindingUnlessInWrapper()
|
void removeBindingUnlessInWrapper()
|
||||||
{
|
{
|
||||||
QBindingStorage *storage = qGetBindingStorage(owner());
|
QBindingStorage *storage = qGetBindingStorage(owner());
|
||||||
@ -576,6 +568,7 @@ public:
|
|||||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||||
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
|
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void notify(const QtPrivate::QPropertyBindingData *binding)
|
void notify(const QtPrivate::QPropertyBindingData *binding)
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,6 @@ private:
|
|||||||
QtPrivate::RefCounted *d;
|
QtPrivate::RefCounted *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class QUntypedPropertyBinding;
|
class QUntypedPropertyBinding;
|
||||||
class QPropertyBindingPrivate;
|
class QPropertyBindingPrivate;
|
||||||
struct QPropertyBindingDataPointer;
|
struct QPropertyBindingDataPointer;
|
||||||
@ -158,6 +157,24 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class QPropertyData;
|
class QPropertyData;
|
||||||
|
|
||||||
|
// Used for grouped property evaluations
|
||||||
|
namespace QtPrivate {
|
||||||
|
class QPropertyBindingData;
|
||||||
|
}
|
||||||
|
struct QPropertyDelayedNotifications;
|
||||||
|
struct QPropertyProxyBindingData
|
||||||
|
{
|
||||||
|
// acts as QPropertyBindingData::d_ptr
|
||||||
|
quintptr d_ptr;
|
||||||
|
/*
|
||||||
|
The two members below store the original binding data and property
|
||||||
|
data pointer of the property which gets proxied.
|
||||||
|
They are set in QPropertyDelayedNotifications::addProperty
|
||||||
|
*/
|
||||||
|
const QtPrivate::QPropertyBindingData *originalBindingData;
|
||||||
|
QUntypedPropertyData *propertyData;
|
||||||
|
};
|
||||||
|
|
||||||
namespace QtPrivate {
|
namespace QtPrivate {
|
||||||
struct BindingEvaluationState;
|
struct BindingEvaluationState;
|
||||||
|
|
||||||
@ -218,6 +235,16 @@ struct QPropertyBindingFunction {
|
|||||||
using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
|
using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
|
||||||
using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
|
using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
A property normally consists of the actual property value and metadata for the binding system.
|
||||||
|
QPropertyBindingData is the latter part. It stores a pointer to either
|
||||||
|
- a (potentially empty) linked list of notifiers, in case there is no binding set,
|
||||||
|
- an actual QUntypedPropertyBinding when the property has a binding,
|
||||||
|
- or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update.
|
||||||
|
|
||||||
|
\sa QPropertyDelayedNotifications, beginPropertyUpdateGroup
|
||||||
|
*/
|
||||||
class Q_CORE_EXPORT QPropertyBindingData
|
class Q_CORE_EXPORT QPropertyBindingData
|
||||||
{
|
{
|
||||||
// Mutable because the address of the observer of the currently evaluating binding is stored here, for
|
// Mutable because the address of the observer of the currently evaluating binding is stored here, for
|
||||||
@ -225,6 +252,7 @@ class Q_CORE_EXPORT QPropertyBindingData
|
|||||||
mutable quintptr d_ptr = 0;
|
mutable quintptr d_ptr = 0;
|
||||||
friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
|
friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
|
||||||
friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
|
friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
|
||||||
|
friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
|
||||||
Q_DISABLE_COPY(QPropertyBindingData)
|
Q_DISABLE_COPY(QPropertyBindingData)
|
||||||
public:
|
public:
|
||||||
QPropertyBindingData() = default;
|
QPropertyBindingData() = default;
|
||||||
@ -232,9 +260,13 @@ public:
|
|||||||
QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
|
QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
|
||||||
~QPropertyBindingData();
|
~QPropertyBindingData();
|
||||||
|
|
||||||
static inline constexpr quintptr BindingBit = 0x1; // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
|
// Is d_ptr pointing to a binding (1) or list of notifiers (0)?
|
||||||
|
static inline constexpr quintptr BindingBit = 0x1;
|
||||||
|
// Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers?
|
||||||
|
static inline constexpr quintptr DelayedNotificationBit = 0x2;
|
||||||
|
|
||||||
bool hasBinding() const { return d_ptr & BindingBit; }
|
bool hasBinding() const { return d_ptr & BindingBit; }
|
||||||
|
bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; }
|
||||||
|
|
||||||
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
|
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
|
||||||
QUntypedPropertyData *propertyDataPtr,
|
QUntypedPropertyData *propertyDataPtr,
|
||||||
@ -243,14 +275,14 @@ public:
|
|||||||
|
|
||||||
QPropertyBindingPrivate *binding() const
|
QPropertyBindingPrivate *binding() const
|
||||||
{
|
{
|
||||||
if (d_ptr & BindingBit)
|
quintptr dd = d();
|
||||||
return reinterpret_cast<QPropertyBindingPrivate*>(d_ptr - BindingBit);
|
if (dd & BindingBit)
|
||||||
|
return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluateIfDirty(const QUntypedPropertyData *property) const;
|
void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
|
||||||
void markDirty();
|
|
||||||
|
|
||||||
void removeBinding()
|
void removeBinding()
|
||||||
{
|
{
|
||||||
@ -267,6 +299,25 @@ public:
|
|||||||
void registerWithCurrentlyEvaluatingBinding() const;
|
void registerWithCurrentlyEvaluatingBinding() const;
|
||||||
void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
|
void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
|
||||||
private:
|
private:
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
Returns a reference to d_ptr, except when d_ptr points to a proxy.
|
||||||
|
In that case, a reference to proxy->d_ptr is returned instead.
|
||||||
|
|
||||||
|
To properly support proxying, direct access to d_ptr only occcurs when
|
||||||
|
- a function actually deals with proxying (e.g.
|
||||||
|
QPropertyDelayedNotifications::addProperty),
|
||||||
|
- only the tag value is accessed (e.g. hasBinding) or
|
||||||
|
- inside a constructor.
|
||||||
|
*/
|
||||||
|
quintptr &d_ref() const
|
||||||
|
{
|
||||||
|
quintptr &d = d_ptr;
|
||||||
|
if (isNotificationDelayed())
|
||||||
|
return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
quintptr d() const { return d_ref(); }
|
||||||
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
|
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
|
||||||
void removeBinding_helper();
|
void removeBinding_helper();
|
||||||
};
|
};
|
||||||
|
@ -241,7 +241,7 @@ void QTimer::start()
|
|||||||
stop();
|
stop();
|
||||||
d->nulltimer = (!d->inter && d->single);
|
d->nulltimer = (!d->inter && d->single);
|
||||||
d->id = QObject::startTimer(d->inter, d->type);
|
d->id = QObject::startTimer(d->inter, d->type);
|
||||||
d->isActiveData.markDirty();
|
d->isActiveData.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -278,7 +278,7 @@ void QTimer::stop()
|
|||||||
if (d->id != INV_TIMER) {
|
if (d->id != INV_TIMER) {
|
||||||
QObject::killTimer(d->id);
|
QObject::killTimer(d->id);
|
||||||
d->id = INV_TIMER;
|
d->id = INV_TIMER;
|
||||||
d->isActiveData.markDirty();
|
d->isActiveData.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,9 +763,8 @@ void QTimer::setInterval(int msec)
|
|||||||
// No need to call markDirty() for d->isActiveData here,
|
// No need to call markDirty() for d->isActiveData here,
|
||||||
// as timer state actually does not change
|
// as timer state actually does not change
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intervalChanged)
|
if (intervalChanged)
|
||||||
d->inter.markDirty();
|
d->inter.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
int QTimer::interval() const
|
int QTimer::interval() const
|
||||||
|
@ -333,7 +333,7 @@ class Q_CORE_EXPORT QVariant
|
|||||||
explicit QVariant(Type type)
|
explicit QVariant(Type type)
|
||||||
: QVariant(QMetaType(int(type)))
|
: QVariant(QMetaType(int(type)))
|
||||||
{}
|
{}
|
||||||
QT_DEPRECATED_VERSION_X_6_0("Use metaType().")
|
QT_DEPRECATED_VERSION_X_6_0("Use typeId() or metaType().")
|
||||||
Type type() const
|
Type type() const
|
||||||
{
|
{
|
||||||
int type = d.typeId();
|
int type = d.typeId();
|
||||||
|
@ -2190,6 +2190,16 @@ inline char qToLower(char ch)
|
|||||||
\sa fromLatin1(), fromLocal8Bit(), fromUtf8()
|
\sa fromLatin1(), fromLocal8Bit(), fromUtf8()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! \fn QString::QString(const char8_t *str)
|
||||||
|
|
||||||
|
Constructs a string initialized with the UTF-8 string \a str. The
|
||||||
|
given const char8_t pointer is converted to Unicode using the
|
||||||
|
fromUtf8() function.
|
||||||
|
|
||||||
|
\since 6.1
|
||||||
|
\sa fromLatin1(), fromLocal8Bit(), fromUtf8()
|
||||||
|
*/
|
||||||
|
|
||||||
/*! \fn QString QString::fromStdString(const std::string &str)
|
/*! \fn QString QString::fromStdString(const std::string &str)
|
||||||
|
|
||||||
Returns a copy of the \a str string. The given string is converted
|
Returns a copy of the \a str string. The given string is converted
|
||||||
@ -5324,6 +5334,14 @@ QString QString::fromLocal8Bit(QByteArrayView ba)
|
|||||||
\sa toUtf8(), fromLatin1(), fromLocal8Bit()
|
\sa toUtf8(), fromLatin1(), fromLocal8Bit()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QString QString::fromUtf8(const char8_t *str)
|
||||||
|
\overload
|
||||||
|
\since 6.1
|
||||||
|
|
||||||
|
This overload is only available when compiling in C++20 mode.
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn QString QString::fromUtf8(const char8_t *str, qsizetype size)
|
\fn QString QString::fromUtf8(const char8_t *str, qsizetype size)
|
||||||
\overload
|
\overload
|
||||||
|
@ -386,6 +386,12 @@ public:
|
|||||||
QString(QChar c);
|
QString(QChar c);
|
||||||
QString(qsizetype size, QChar c);
|
QString(qsizetype size, QChar c);
|
||||||
inline QString(QLatin1String latin1);
|
inline QString(QLatin1String latin1);
|
||||||
|
#if defined(__cpp_char8_t) || defined(Q_CLANG_QDOC)
|
||||||
|
Q_WEAK_OVERLOAD
|
||||||
|
inline QString(const char8_t *str)
|
||||||
|
: QString(fromUtf8(str))
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
inline QString(const QString &) noexcept;
|
inline QString(const QString &) noexcept;
|
||||||
inline ~QString();
|
inline ~QString();
|
||||||
QString &operator=(QChar c);
|
QString &operator=(QChar c);
|
||||||
@ -746,6 +752,9 @@ public:
|
|||||||
return fromUtf8(QByteArrayView(utf8, !utf8 || size < 0 ? qstrlen(utf8) : size));
|
return fromUtf8(QByteArrayView(utf8, !utf8 || size < 0 ? qstrlen(utf8) : size));
|
||||||
}
|
}
|
||||||
#if defined(__cpp_char8_t) || defined(Q_CLANG_QDOC)
|
#if defined(__cpp_char8_t) || defined(Q_CLANG_QDOC)
|
||||||
|
Q_WEAK_OVERLOAD
|
||||||
|
static inline QString fromUtf8(const char8_t *str)
|
||||||
|
{ return fromUtf8(reinterpret_cast<const char *>(str)); }
|
||||||
Q_WEAK_OVERLOAD
|
Q_WEAK_OVERLOAD
|
||||||
static inline QString fromUtf8(const char8_t *str, qsizetype size)
|
static inline QString fromUtf8(const char8_t *str, qsizetype size)
|
||||||
{ return fromUtf8(reinterpret_cast<const char *>(str), size); }
|
{ return fromUtf8(reinterpret_cast<const char *>(str), size); }
|
||||||
|
@ -2578,11 +2578,10 @@ size_t qHash(long double key, size_t seed) noexcept
|
|||||||
hashes. A multi-valued hash is a hash that allows multiple values
|
hashes. A multi-valued hash is a hash that allows multiple values
|
||||||
with the same key.
|
with the same key.
|
||||||
|
|
||||||
Because QMultiHash inherits QHash, all of QHash's functionality also
|
QMultiHash mostly mirrors QHash's API. For example, you can use isEmpty() to test
|
||||||
applies to QMultiHash. For example, you can use isEmpty() to test
|
|
||||||
whether the hash is empty, and you can traverse a QMultiHash using
|
whether the hash is empty, and you can traverse a QMultiHash using
|
||||||
QHash's iterator classes (for example, QHashIterator). But opposed to
|
QHash's iterator classes (for example, QHashIterator). But opposed to
|
||||||
QHash, it provides an insert() function will allow the insertion of
|
QHash, it provides an insert() function that allows the insertion of
|
||||||
multiple items with the same key. The replace() function corresponds to
|
multiple items with the same key. The replace() function corresponds to
|
||||||
QHash::insert(). It also provides convenient operator+() and
|
QHash::insert(). It also provides convenient operator+() and
|
||||||
operator+=().
|
operator+=().
|
||||||
|
@ -137,10 +137,10 @@ public:
|
|||||||
// libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
|
// libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
|
||||||
#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
|
#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
|
||||||
using iterator_category = std::contiguous_iterator_tag;
|
using iterator_category = std::contiguous_iterator_tag;
|
||||||
|
using element_type = value_type;
|
||||||
#else
|
#else
|
||||||
using iterator_category = std::random_access_iterator_tag;
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
#endif
|
#endif
|
||||||
using element_type = value_type;
|
|
||||||
|
|
||||||
using pointer = T *;
|
using pointer = T *;
|
||||||
using reference = T &;
|
using reference = T &;
|
||||||
@ -179,10 +179,10 @@ public:
|
|||||||
// libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
|
// libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
|
||||||
#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
|
#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
|
||||||
using iterator_category = std::contiguous_iterator_tag;
|
using iterator_category = std::contiguous_iterator_tag;
|
||||||
|
using element_type = const value_type;
|
||||||
#else
|
#else
|
||||||
using iterator_category = std::random_access_iterator_tag;
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
#endif
|
#endif
|
||||||
using element_type = const value_type;
|
|
||||||
using pointer = const T *;
|
using pointer = const T *;
|
||||||
using reference = const T &;
|
using reference = const T &;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtGui module of the Qt Toolkit.
|
** This file is part of the QtGui module of the Qt Toolkit.
|
||||||
@ -258,7 +258,7 @@ struct QBidiAlgorithm {
|
|||||||
for (int i = 0; i < length; ++i) {
|
for (int i = 0; i < length; ++i) {
|
||||||
int pos = i;
|
int pos = i;
|
||||||
char32_t uc = text[i].unicode();
|
char32_t uc = text[i].unicode();
|
||||||
if (QChar::isHighSurrogate(uc) && i < length - 1) {
|
if (QChar::isHighSurrogate(uc) && i < length - 1 && text[i + 1].isLowSurrogate()) {
|
||||||
++i;
|
++i;
|
||||||
analysis[i].bidiDirection = QChar::DirNSM;
|
analysis[i].bidiDirection = QChar::DirNSM;
|
||||||
uc = QChar::surrogateToUcs4(ushort(uc), text[i].unicode());
|
uc = QChar::surrogateToUcs4(ushort(uc), text[i].unicode());
|
||||||
|
@ -61,13 +61,6 @@
|
|||||||
|
|
||||||
// including jpeglib.h seems to be a little messy
|
// including jpeglib.h seems to be a little messy
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// jpeglib.h->jmorecfg.h tries to typedef int boolean; but this conflicts with
|
|
||||||
// some Windows headers that may or may not have been included
|
|
||||||
#ifdef HAVE_BOOLEAN
|
|
||||||
# undef HAVE_BOOLEAN
|
|
||||||
#endif
|
|
||||||
#define boolean jboolean
|
|
||||||
|
|
||||||
#define XMD_H // shut JPEGlib up
|
#define XMD_H // shut JPEGlib up
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
#ifdef const
|
#ifdef const
|
||||||
|
@ -95,7 +95,9 @@ private slots:
|
|||||||
void noFakeDependencies();
|
void noFakeDependencies();
|
||||||
|
|
||||||
void bindablePropertyWithInitialization();
|
void bindablePropertyWithInitialization();
|
||||||
void markDirty();
|
void noDoubleNotification();
|
||||||
|
void groupedNotifications();
|
||||||
|
void groupedNotificationConsistency();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QProperty::functorBinding()
|
void tst_QProperty::functorBinding()
|
||||||
@ -129,8 +131,8 @@ void tst_QProperty::multipleDependencies()
|
|||||||
QProperty<int> sum;
|
QProperty<int> sum;
|
||||||
sum.setBinding([&]() { return firstDependency + secondDependency; });
|
sum.setBinding([&]() { return firstDependency + secondDependency; });
|
||||||
|
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 0);
|
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(secondDependency).observerCount(), 0);
|
QCOMPARE(QPropertyBindingDataPointer::get(secondDependency).observerCount(), 1);
|
||||||
|
|
||||||
QCOMPARE(sum.value(), int(3));
|
QCOMPARE(sum.value(), int(3));
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
QCOMPARE(QPropertyBindingDataPointer::get(firstDependency).observerCount(), 1);
|
||||||
@ -260,12 +262,12 @@ void tst_QProperty::avoidDependencyAllocationAfterFirstEval()
|
|||||||
|
|
||||||
QCOMPARE(propWithBinding.value(), int(11));
|
QCOMPARE(propWithBinding.value(), int(11));
|
||||||
|
|
||||||
QVERIFY(QPropertyBindingDataPointer::get(propWithBinding).bindingPtr());
|
QVERIFY(QPropertyBindingDataPointer::get(propWithBinding).binding());
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(propWithBinding).bindingPtr()->dependencyObserverCount, 2u);
|
QCOMPARE(QPropertyBindingDataPointer::get(propWithBinding).binding()->dependencyObserverCount, 2u);
|
||||||
|
|
||||||
firstDependency = 100;
|
firstDependency = 100;
|
||||||
QCOMPARE(propWithBinding.value(), int(110));
|
QCOMPARE(propWithBinding.value(), int(110));
|
||||||
QCOMPARE(QPropertyBindingDataPointer::get(propWithBinding).bindingPtr()->dependencyObserverCount, 2u);
|
QCOMPARE(QPropertyBindingDataPointer::get(propWithBinding).binding()->dependencyObserverCount, 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QProperty::boolProperty()
|
void tst_QProperty::boolProperty()
|
||||||
@ -484,23 +486,22 @@ class BindingLoopTester : public QObject
|
|||||||
|
|
||||||
void tst_QProperty::bindingLoop()
|
void tst_QProperty::bindingLoop()
|
||||||
{
|
{
|
||||||
QScopedPointer<QProperty<int>> firstProp;
|
QProperty<int> firstProp;
|
||||||
|
|
||||||
QProperty<int> secondProp([&]() -> int {
|
QProperty<int> secondProp([&]() -> int {
|
||||||
return firstProp ? firstProp->value() : 0;
|
return firstProp.value();
|
||||||
});
|
});
|
||||||
|
|
||||||
QProperty<int> thirdProp([&]() -> int {
|
QProperty<int> thirdProp([&]() -> int {
|
||||||
return secondProp.value();
|
return secondProp.value();
|
||||||
});
|
});
|
||||||
|
|
||||||
firstProp.reset(new QProperty<int>());
|
firstProp.setBinding([&]() -> int {
|
||||||
firstProp->setBinding([&]() -> int {
|
return secondProp.value() + thirdProp.value();
|
||||||
return secondProp.value();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QCOMPARE(thirdProp.value(), 0);
|
thirdProp.setValue(10);
|
||||||
QCOMPARE(secondProp.binding().error().type(), QPropertyBindingError::BindingLoop);
|
QCOMPARE(firstProp.binding().error().type(), QPropertyBindingError::BindingLoop);
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1200,7 +1201,9 @@ void tst_QProperty::qobjectObservers()
|
|||||||
MyQObject object;
|
MyQObject object;
|
||||||
int onValueChangedCalled = 0;
|
int onValueChangedCalled = 0;
|
||||||
{
|
{
|
||||||
auto handler = object.bindableFoo().onValueChanged([&onValueChangedCalled]() { ++onValueChangedCalled;});
|
auto handler = object.bindableFoo().onValueChanged([&onValueChangedCalled]() {
|
||||||
|
++onValueChangedCalled;
|
||||||
|
});
|
||||||
QCOMPARE(onValueChangedCalled, 0);
|
QCOMPARE(onValueChangedCalled, 0);
|
||||||
|
|
||||||
object.setFoo(10);
|
object.setFoo(10);
|
||||||
@ -1606,80 +1609,103 @@ void tst_QProperty::bindablePropertyWithInitialization()
|
|||||||
QCOMPARE(tester.prop3().anotherValue, 20);
|
QCOMPARE(tester.prop3().anotherValue, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MarkDirtyTester : public QObject
|
void tst_QProperty::noDoubleNotification()
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
/* dependency graph for this test
|
||||||
public:
|
x --> y means y depends on x
|
||||||
Q_PROPERTY(int value1 READ value1 WRITE setValue1 BINDABLE bindableValue1)
|
a-->b-->d
|
||||||
Q_PROPERTY(int value2 READ value2 WRITE setValue1 BINDABLE bindableValue2)
|
\ ^
|
||||||
Q_PROPERTY(int computed READ computed BINDABLE bindableComputed)
|
\->c--/
|
||||||
|
*/
|
||||||
|
QProperty<int> a(0);
|
||||||
|
QProperty<int> b;
|
||||||
|
b.setBinding([&](){ return a.value(); });
|
||||||
|
QProperty<int> c;
|
||||||
|
c.setBinding([&](){ return a.value(); });
|
||||||
|
QProperty<int> d;
|
||||||
|
d.setBinding([&](){ return b.value() + c.value(); });
|
||||||
|
int nNotifications = 0;
|
||||||
|
int expected = 0;
|
||||||
|
auto connection = d.subscribe([&](){
|
||||||
|
++nNotifications;
|
||||||
|
QCOMPARE(d.value(), expected);
|
||||||
|
});
|
||||||
|
QCOMPARE(nNotifications, 1);
|
||||||
|
expected = 2;
|
||||||
|
a = 1;
|
||||||
|
QCOMPARE(nNotifications, 2);
|
||||||
|
expected = 4;
|
||||||
|
a = 2;
|
||||||
|
QCOMPARE(nNotifications, 3);
|
||||||
|
}
|
||||||
|
|
||||||
inline static int staticValue = 0;
|
void tst_QProperty::groupedNotifications()
|
||||||
|
|
||||||
int value1() const {return m_value1;}
|
|
||||||
void setValue1(int val) {m_value1 = val;}
|
|
||||||
QBindable<int> bindableValue1() {return { &m_value1 };}
|
|
||||||
|
|
||||||
int value2() const {return m_value2;}
|
|
||||||
void setValue2(int val) { m_value2.setValue(val); }
|
|
||||||
QBindable<int> bindableValue2() {return { &m_value2 };}
|
|
||||||
|
|
||||||
int computed() const { return staticValue + m_value1; }
|
|
||||||
QBindable<int> bindableComputed() {return {&m_computed};}
|
|
||||||
|
|
||||||
void incrementStaticValue() {
|
|
||||||
++staticValue;
|
|
||||||
m_computed.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void markValue1Dirty() {
|
|
||||||
m_value1.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void markValue2Dirty() {
|
|
||||||
m_value2.markDirty();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(MarkDirtyTester, int, m_value1, nullptr)
|
|
||||||
Q_OBJECT_COMPAT_PROPERTY(MarkDirtyTester, int, m_value2, &MarkDirtyTester::setValue2)
|
|
||||||
Q_OBJECT_COMPUTED_PROPERTY(MarkDirtyTester, int, m_computed, &MarkDirtyTester::computed)
|
|
||||||
};
|
|
||||||
|
|
||||||
void tst_QProperty::markDirty()
|
|
||||||
{
|
{
|
||||||
{
|
QProperty<int> a(0);
|
||||||
QProperty<int> testProperty;
|
QProperty<int> b;
|
||||||
int changeCounter = 0;
|
b.setBinding([&](){ return a.value(); });
|
||||||
auto handler = testProperty.onValueChanged([&](){++changeCounter;});
|
QProperty<int> c;
|
||||||
testProperty.markDirty();
|
c.setBinding([&](){ return a.value(); });
|
||||||
QCOMPARE(changeCounter, 1);
|
QProperty<int> d;
|
||||||
}
|
QProperty<int> e;
|
||||||
{
|
e.setBinding([&](){ return b.value() + c.value() + d.value(); });
|
||||||
MarkDirtyTester dirtyTester;
|
int nNotifications = 0;
|
||||||
int computedChangeCounter = 0;
|
int expected = 0;
|
||||||
int value1ChangeCounter = 0;
|
auto connection = e.subscribe([&](){
|
||||||
auto handler = dirtyTester.bindableComputed().onValueChanged([&](){
|
++nNotifications;
|
||||||
computedChangeCounter++;
|
QCOMPARE(e.value(), expected);
|
||||||
});
|
});
|
||||||
auto handler2 = dirtyTester.bindableValue1().onValueChanged([&](){
|
QCOMPARE(nNotifications, 1);
|
||||||
value1ChangeCounter++;
|
|
||||||
});
|
expected = 2;
|
||||||
dirtyTester.incrementStaticValue();
|
Qt::beginPropertyUpdateGroup();
|
||||||
QCOMPARE(computedChangeCounter, 1);
|
a = 1;
|
||||||
QCOMPARE(dirtyTester.computed(), 1);
|
QCOMPARE(b.value(), 0);
|
||||||
dirtyTester.markValue1Dirty();
|
QCOMPARE(c.value(), 0);
|
||||||
QCOMPARE(value1ChangeCounter, 1);
|
QCOMPARE(d.value(), 0);
|
||||||
QCOMPARE(computedChangeCounter, 1);
|
QCOMPARE(nNotifications, 1);
|
||||||
}
|
Qt::endPropertyUpdateGroup();
|
||||||
{
|
QCOMPARE(b.value(), 1);
|
||||||
MarkDirtyTester dirtyTester;
|
QCOMPARE(c.value(), 1);
|
||||||
int changeCounter = 0;
|
QCOMPARE(e.value(), 2);
|
||||||
auto handler = dirtyTester.bindableValue2().onValueChanged([&](){
|
QCOMPARE(nNotifications, 2);
|
||||||
changeCounter++;
|
|
||||||
});
|
expected = 7;
|
||||||
dirtyTester.markValue2Dirty();
|
Qt::beginPropertyUpdateGroup();
|
||||||
QCOMPARE(changeCounter, 1);
|
a = 2;
|
||||||
}
|
d = 3;
|
||||||
|
QCOMPARE(b.value(), 1);
|
||||||
|
QCOMPARE(c.value(), 1);
|
||||||
|
QCOMPARE(d.value(), 3);
|
||||||
|
QCOMPARE(nNotifications, 2);
|
||||||
|
Qt::endPropertyUpdateGroup();
|
||||||
|
QCOMPARE(b.value(), 2);
|
||||||
|
QCOMPARE(c.value(), 2);
|
||||||
|
QCOMPARE(e.value(), 7);
|
||||||
|
QCOMPARE(nNotifications, 3);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QProperty::groupedNotificationConsistency()
|
||||||
|
{
|
||||||
|
QProperty<int> i(0);
|
||||||
|
QProperty<int> j(0);
|
||||||
|
bool areEqual = true;
|
||||||
|
|
||||||
|
auto observer = i.onValueChanged([&](){
|
||||||
|
areEqual = i == j;
|
||||||
|
});
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
j = 1;
|
||||||
|
QVERIFY(!areEqual); // value changed runs before j = 1
|
||||||
|
|
||||||
|
Qt::beginPropertyUpdateGroup();
|
||||||
|
i = 2;
|
||||||
|
j = 2;
|
||||||
|
Qt::endPropertyUpdateGroup();
|
||||||
|
QVERIFY(areEqual); // value changed runs after everything has been evaluated
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QProperty);
|
QTEST_MAIN(tst_QProperty);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
SOURCES += main.cpp
|
SOURCES += main.cpp
|
||||||
QT = core
|
QT = core
|
||||||
CONFIG += console
|
CONFIG += console
|
||||||
|
DEFINES += QT_FORCE_ASSERTS
|
||||||
|
Loading…
Reference in New Issue
Block a user