aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/imports/builtins/jsroot.qmltypes1
-rw-r--r--src/labs/folderlistmodel/qquickfolderlistmodel.cpp57
-rw-r--r--src/labs/models/doc/src/qmllabsmodels.qdoc4
-rw-r--r--src/qml/Qt6QmlMacros.cmake44
-rw-r--r--src/qml/common/qqmljsmemorypool_p.h10
-rw-r--r--src/qml/doc/src/cppintegration/topic.qdoc3
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc6
-rw-r--r--src/qml/doc/src/qtqml-writing-a-module.qdoc2
-rw-r--r--src/qml/jsapi/qjsengine.cpp5
-rw-r--r--src/qml/jsapi/qjsvalue.cpp3
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp38
-rw-r--r--src/qml/jsruntime/qv4engine.cpp49
-rw-r--r--src/qml/jsruntime/qv4engine_p.h4
-rw-r--r--src/qml/jsruntime/qv4identifierhash.cpp4
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp13
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h3
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp18
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp129
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h34
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp7
-rw-r--r--src/qml/jsruntime/qv4string_p.h22
-rw-r--r--src/qml/qml/ftw/qrecyclepool_p.h9
-rw-r--r--src/qml/qml/qqmldata_p.h64
-rw-r--r--src/qml/qml/qqmlengine.cpp85
-rw-r--r--src/qml/qml/qqmlengine_p.h13
-rw-r--r--src/qml/qml/qqmlimport.cpp9
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp46
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp2
-rw-r--r--src/qml/qml/qqmlpropertybinding.cpp19
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp29
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h1
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h1
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp2
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp24
-rw-r--r--src/qmldom/qqmldomreformatter.cpp40
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp3
-rw-r--r--src/quick/doc/images/qml-item-canvas-startAngle.pngbin5099 -> 5826 bytes
-rw-r--r--src/quick/doc/snippets/qml/images/qt-logo.pngbin0 -> 6208 bytes
-rw-r--r--src/quick/doc/snippets/qml/item/itemGrab.qml (renamed from src/quick/doc/snippets/qml/itemGrab.qml)43
-rw-r--r--src/quick/doc/snippets/qml/nestedWindowTransientParent.qml70
-rw-r--r--src/quick/doc/snippets/qml/splashWindow.qml104
-rw-r--r--src/quick/doc/snippets/qml/windowActiveAttached.qml57
-rw-r--r--src/quick/doc/snippets/qml/windowPalette.qml85
-rw-r--r--src/quick/doc/snippets/qml/windowVisibility.qml76
-rw-r--r--src/quick/doc/src/concepts/positioning/righttoleft.qdoc10
-rw-r--r--src/quick/doc/src/examples.qdoc1
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp4
-rw-r--r--src/quick/items/qquickflickable.cpp20
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickimage.cpp1
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp12
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp2
-rw-r--r--src/quick/items/qquicklistview.cpp4
-rw-r--r--src/quick/items/qquickpalette.cpp27
-rw-r--r--src/quick/items/qquickscreen.cpp2
-rw-r--r--src/quick/items/qquicktextedit.cpp12
-rw-r--r--src/quick/items/qquicktextinput.cpp13
-rw-r--r--src/quick/items/qquickwindow.cpp42
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp12
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp2
-rw-r--r--src/quick/util/qquickanimation.cpp41
-rw-r--r--src/quick/util/qquickanimation_p_p.h2
-rw-r--r--src/quick/util/qquickapplication.cpp1
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp7
-rw-r--r--src/quick/util/qquicksystempalette.cpp4
-rw-r--r--src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml67
-rw-r--r--src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc76
-rw-r--r--src/quickdialogs2/quickdialogs2/CMakeLists.txt1
-rw-r--r--src/quicktemplates2/qquickdeferredexecute.cpp18
-rw-r--r--src/quicktemplates2/qquickheaderview.cpp94
-rw-r--r--src/quicktemplates2/qquickmenu.cpp6
-rw-r--r--src/quicktemplates2/qquickpopup.cpp1
-rw-r--r--src/quicktemplates2/qquickswipeview.cpp41
-rw-r--r--src/quicktemplates2/qquickswipeview_p.h1
-rw-r--r--src/quicktestutils/quick/viewtestutils.cpp87
-rw-r--r--src/quicktestutils/quick/viewtestutils_p.h11
-rw-r--r--src/quickwidgets/qquickwidget.cpp6
79 files changed, 1398 insertions, 474 deletions
diff --git a/src/imports/builtins/jsroot.qmltypes b/src/imports/builtins/jsroot.qmltypes
index 0a8b70babe..39878d8cf3 100644
--- a/src/imports/builtins/jsroot.qmltypes
+++ b/src/imports/builtins/jsroot.qmltypes
@@ -1356,6 +1356,7 @@ Module {
Method { name: "formatTime" }
Method { name: "formatDateTime" }
Method { name: "locale" }
+ Method { name: "url" }
Method { name: "resolvedUrl" }
Method { name: "openUrlExternally" }
Method { name: "font" }
diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
index b86ddfe397..b05613b410 100644
--- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp
@@ -252,16 +252,16 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Components access names and paths via the following roles:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
Additionally a file entry can be differentiated from a folder entry via the
@@ -304,6 +304,7 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
Component {
id: fileDelegate
+ required property string fileName
Text { text: fileName }
}
@@ -395,6 +396,7 @@ QHash<int, QByteArray> QQuickFolderListModel::roleNames() const
/*!
\qmlproperty int FolderListModel::count
+ \readonly
Returns the number of items in the current folder that match the
filter criteria.
@@ -419,7 +421,8 @@ QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) con
The value must be a \c file: or \c qrc: URL, or a relative URL.
- The default value is an invalid URL.
+ The default value is the application's working directory at the time
+ when the FolderListModel is first initialized.
*/
QUrl QQuickFolderListModel::folder() const
{
@@ -493,6 +496,7 @@ void QQuickFolderListModel::setRootFolder(const QUrl &path)
/*!
\qmlproperty url FolderListModel::parentFolder
+ \readonly
Returns the URL of the parent of the current \l folder.
*/
@@ -566,11 +570,11 @@ void QQuickFolderListModel::componentComplete()
The \a sortField property contains the field to use for sorting.
\c sortField may be one of:
- \value Unsorted no sorting is applied
- \value Name sort by filename
- \value Time sort by time modified
- \value Size sort by file size
- \value Type sort by file type (extension)
+ \value FolderListModel.Unsorted no sorting is applied
+ \value FolderListModel.Name sort by filename (default)
+ \value FolderListModel.Time sort by time modified
+ \value FolderListModel.Size sort by file size
+ \value FolderListModel.Type sort by file type/extension
\sa sortReversed
*/
@@ -818,6 +822,7 @@ void QQuickFolderListModel::setCaseSensitive(bool on)
/*!
\qmlproperty enumeration FolderListModel::status
\since 5.11
+ \readonly
This property holds the status of folder reading. It can be one of:
@@ -884,16 +889,16 @@ void QQuickFolderListModel::setSortCaseSensitive(bool on)
are available:
\list
- \li \c fileName
- \li \c filePath
- \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15)
- \li \c fileUrl (since Qt 5.15)
- \li \c fileBaseName
- \li \c fileSuffix
- \li \c fileSize
- \li \c fileModified
- \li \c fileAccessed
- \li \c fileIsDir
+ \li \c fileName (\c string)
+ \li \c filePath (\c string)
+ \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15)
+ \li \c fileUrl (\c url) (since Qt 5.15)
+ \li \c fileBaseName (\c string)
+ \li \c fileSuffix (\c string)
+ \li \c fileSize (\c qlonglong)
+ \li \c fileModified (\c date)
+ \li \c fileAccessed (\c date)
+ \li \c fileIsDir (\c bool)
\endlist
*/
QVariant QQuickFolderListModel::get(int idx, const QString &property) const
diff --git a/src/labs/models/doc/src/qmllabsmodels.qdoc b/src/labs/models/doc/src/qmllabsmodels.qdoc
index 598786f1f6..f2b57323dc 100644
--- a/src/labs/models/doc/src/qmllabsmodels.qdoc
+++ b/src/labs/models/doc/src/qmllabsmodels.qdoc
@@ -36,7 +36,7 @@
To use the types in this module, import the module with the following line:
- \code
+ \qml
import Qt.labs.qmlmodels
- \endcode
+ \endqml
*/
diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake
index bda7b9b83a..d805d37feb 100644
--- a/src/qml/Qt6QmlMacros.cmake
+++ b/src/qml/Qt6QmlMacros.cmake
@@ -1053,7 +1053,7 @@ function(qt6_add_qml_plugin target)
if(NOT arg_CLASS_NAME)
if(NOT "${arg_BACKING_TARGET}" STREQUAL "")
- get_target_property(arg_CLASS_NAME ${target} QT_QML_MODULE_CLASS_NAME)
+ get_target_property(arg_CLASS_NAME ${arg_BACKING_TARGET} QT_QML_MODULE_CLASS_NAME)
endif()
if(NOT arg_CLASS_NAME)
_qt_internal_compute_qml_plugin_class_name_from_uri("${arg_URI}" arg_CLASS_NAME)
@@ -1429,9 +1429,11 @@ function(qt6_target_qml_sources target)
get_filename_component(file_out_dir ${file_out} DIRECTORY)
file(MAKE_DIRECTORY ${file_out_dir})
- execute_process(COMMAND
- ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
- )
+ if(EXISTS "${file_absolute}")
+ execute_process(COMMAND
+ ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out}
+ )
+ endif()
add_custom_command(OUTPUT ${file_out}
COMMAND ${CMAKE_COMMAND} -E copy ${file_src} ${file_out}
@@ -1518,6 +1520,12 @@ function(qt6_target_qml_sources target)
get_source_file_property(qml_file_singleton ${qml_file_src} QT_QML_SINGLETON_TYPE)
get_source_file_property(qml_file_internal ${qml_file_src} QT_QML_INTERNAL_TYPE)
+ if (qml_file_singleton AND qml_file_internal)
+ message(FATAL_ERROR
+ "${qml_file_src} is marked as both internal and as a "
+ "singleton, but singletons cannot be internal!")
+ endif()
+
if (NOT qml_file_versions)
set(qml_file_versions ${qml_module_files_versions})
endif()
@@ -1822,28 +1830,6 @@ function(_qt_internal_qml_type_registration target)
message(FATAL_ERROR "Need target metatypes.json file")
endif()
- cmake_policy(PUSH)
-
- set(registration_cpp_file_dep_args)
- if (CMAKE_GENERATOR MATCHES "Ninja" OR
- (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles"))
- if(POLICY CMP0116)
- # Without explicitly setting this policy to NEW, we get a warning
- # even though we ensure there's actually no problem here.
- # See https://gitlab.kitware.com/cmake/cmake/-/issues/21959
- cmake_policy(SET CMP0116 NEW)
- set(relative_to_dir ${CMAKE_CURRENT_BINARY_DIR})
- else()
- set(relative_to_dir ${CMAKE_BINARY_DIR})
- endif()
- set(dependency_file_cpp "${target_binary_dir}/qmltypes/${type_registration_cpp_file_name}.d")
- set(registration_cpp_file_dep_args DEPFILE ${dependency_file_cpp})
- file(RELATIVE_PATH cpp_file_name "${relative_to_dir}" "${type_registration_cpp_file}")
- file(GENERATE OUTPUT "${dependency_file_cpp}"
- CONTENT "${cpp_file_name}: $<IF:$<BOOL:${genex_list}>,\\\n$<JOIN:${genex_list}, \\\n>, \\\n>"
- )
- endif()
-
add_custom_command(
OUTPUT
${type_registration_cpp_file}
@@ -1863,12 +1849,9 @@ function(_qt_internal_qml_type_registration target)
${CMAKE_COMMAND} -E make_directory "${generated_marker_dir}"
COMMAND
${CMAKE_COMMAND} -E touch "${generated_marker_file}"
- ${registration_cpp_file_dep_args}
COMMENT "Automatic QML type registration for target ${target}"
)
- cmake_policy(POP)
-
# The ${target}_qmllint targets need to depend on the generation of all
# *.qmltypes files in the build. We have no way of reliably working out
# which QML modules a given target depends on at configure time, so we
@@ -2166,7 +2149,8 @@ but this file does not exist. Possible reasons include:
# across those libraries to the end target (executable or shared library).
# The plugin initializers will be linked via usage requirements from the plugin target.
get_target_property(target_type ${target} TYPE)
- if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY")
+ if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY"
+ OR target_type STREQUAL "MODULE_LIBRARY")
set(link_type "PRIVATE")
else()
set(link_type "INTERFACE")
diff --git a/src/qml/common/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h
index dcf3fafb67..2a1dc43110 100644
--- a/src/qml/common/qqmljsmemorypool_p.h
+++ b/src/qml/common/qqmljsmemorypool_p.h
@@ -81,13 +81,12 @@ public:
free(_blocks);
}
- qDeleteAll(strings);
}
inline void *allocate(size_t size)
{
size = (size + 7) & ~size_t(7);
- if (Q_LIKELY(_ptr && (_ptr + size < _end))) {
+ if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) {
void *addr = _ptr;
_ptr += size;
return addr;
@@ -105,9 +104,8 @@ public:
template <typename Tp, typename... Ta> Tp *New(Ta... args)
{ return new (this->allocate(sizeof(Tp))) Tp(args...); }
- QStringView newString(const QString &string) {
- strings.append(new QString(string));
- return QStringView(*strings.last());
+ QStringView newString(QString string) {
+ return strings.emplace_back(std::move(string));
}
private:
@@ -151,7 +149,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
- QVector<QString*> strings;
+ QStringList strings;
enum
{
diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc
index 14a27c9a3f..1a77039e7c 100644
--- a/src/qml/doc/src/cppintegration/topic.qdoc
+++ b/src/qml/doc/src/cppintegration/topic.qdoc
@@ -205,7 +205,8 @@ dynamically load and introspect objects through the Qt meta object system.
\include warning.qdocinc
For more information on accessing QML objects from C++, see the documentation on
-\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}.
+\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++},
+and the \l {Exposing Data from C++ to QML} section of the Best Practices page.
\section1 Data Type Conversion Between QML and C++
diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
index 493b031b60..6c235ef388 100644
--- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
+++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc
@@ -177,7 +177,6 @@ An identified module has several restrictions upon it:
\li the module must register its types into the module identifier type
namespace
\li the module may not register types into any other module's namespace
-\li clients must specify a version when importing the module
\endlist
For example, if an identified module is installed into
@@ -194,10 +193,9 @@ module com.example.CustomUi
\endcode
Clients will then be able to import the above module with the following import
-statement (assuming that the module registers types into version 1.0 of its
-namespace):
+statement:
\qml
-import com.example.CustomUi 1.0
+import com.example.CustomUi
\endqml
*/
diff --git a/src/qml/doc/src/qtqml-writing-a-module.qdoc b/src/qml/doc/src/qtqml-writing-a-module.qdoc
index 6ad820552e..a3464926e1 100644
--- a/src/qml/doc/src/qtqml-writing-a-module.qdoc
+++ b/src/qml/doc/src/qtqml-writing-a-module.qdoc
@@ -116,7 +116,7 @@ You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbo
Add the following code to the main.cpp:
\badcode
-#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/QQmlExtensionPlugin>
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
\endcode
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 2c6c579e4f..cdcb5b3bff 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -509,6 +509,9 @@ static QUrl urlForFileName(const QString &fileName)
The script code will be evaluated in the context of the global object.
+ \note If you need to evaluate inside a QML context, use \l QQmlExpression
+ instead.
+
The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the
engine; in this case the return value will be the exception
that was thrown (typically an \c{Error} object; see
@@ -539,6 +542,8 @@ static QUrl urlForFileName(const QString &fileName)
exception value will still be returned. Use \c exceptionStackTrace->isEmpty()
to distinguish whether the value was a normal or an exceptional return
value.
+
+ \sa QQmlExpression::evaluate
*/
QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace)
{
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 832eaca8da..f9df4a40c5 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -200,10 +200,11 @@
/*!
\enum QJSValue::ObjectConversionBehavior
- This enum is used to specify how JavaScript objects without an equivalent
+ This enum is used to specify how JavaScript objects and symbols without an equivalent
native Qt type should be treated when converting to QVariant.
\value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted.
+ Symbols are converted to QString.
\value RetainJSObjects The value is retained as QJSValue wrapped in QVariant.
*/
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 20501f9d03..224c898536 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -670,8 +670,8 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
return p1s->toQString() < p2s->toQString();
}
-template <typename RandomAccessIterator, typename T, typename LessThan>
-void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan)
+template <typename RandomAccessIterator, typename LessThan>
+void sortHelper(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
{
top:
int span = int(end - start);
@@ -716,7 +716,7 @@ top:
++low;
qSwap(*end, *low);
- sortHelper(start, low, t, lessThan);
+ sortHelper(start, low, lessThan);
start = low + 1;
++end;
@@ -816,8 +816,36 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn));
- Value *begin = thisObject->arrayData()->values.values;
- sortHelper(begin, begin + len, *begin, lessThan);
+ const auto thisArrayData = thisObject->arrayData();
+ uint startIndex = thisArrayData->mappedIndex(0);
+ uint endIndex = thisArrayData->mappedIndex(len - 1) + 1;
+ if (startIndex < endIndex) {
+ // Values are contiguous. Sort right away.
+ sortHelper(
+ thisArrayData->values.values + startIndex,
+ thisArrayData->values.values + endIndex,
+ lessThan);
+ } else {
+ // Values wrap around the end of the allocation. Close the gap to form a contiguous array.
+ // We're going to sort anyway. So we don't need to care about order.
+
+ // ArrayElementLessThan sorts empty and undefined to the end of the array anyway, but we
+ // probably shouldn't rely on the unused slots to be actually undefined or empty.
+
+ const uint gap = startIndex - endIndex;
+ const uint allocEnd = thisArrayData->values.alloc - 1;
+ for (uint i = 0; i < gap; ++i) {
+ const uint from = allocEnd - i;
+ const uint to = endIndex + i;
+ if (from < startIndex)
+ break;
+
+ std::swap(thisArrayData->values.values[from], thisArrayData->values.values[to]);
+ }
+
+ thisArrayData->offset = 0;
+ sortHelper(thisArrayData->values.values, thisArrayData->values.values + len, lessThan);
+ }
#ifdef CHECK_SPARSE_ARRAYS
thisObject->initSparseArray();
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c047298aba..0927687578 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1506,7 +1506,9 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
// Variant conversion code
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
+static QVariant toVariant(
+ QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint,
+ bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects);
static QObject *qtObjectFromJS(const QV4::Value &value);
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
@@ -1518,7 +1520,9 @@ static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &
return v4->metaTypeToJS(value.metaType(), value.constData());
}
-static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects, V4ObjectSet *visitedObjects)
+static QVariant toVariant(
+ QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType,
+ bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects)
{
Q_ASSERT (!value.isEmpty());
QV4::Scope scope(e);
@@ -1657,6 +1661,9 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return *ld->d()->locale;
#endif
if (const QV4::DateObject *d = value.as<DateObject>()) {
+ // NOTE: since we convert QTime to JS Date,
+ // round trip will change the variant type (to QDateTime)!
+
auto dt = d->toQDateTime();
// See ExecutionEngine::metaTypeFromJS()'s handling of QMetaType::Date:
if (typeHint == QMetaType::QDate) {
@@ -1670,7 +1677,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return d->toQUrl();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
- // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
+ if (const Symbol *symbol = value.as<Symbol>()) {
+ return createJSValueForObjectsAndSymbols
+ ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue()))
+ : symbol->descriptiveString();
+ }
QV4::ScopedObject o(scope, value);
Q_ASSERT(o);
@@ -1680,16 +1691,17 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet
return re->toQRegularExpression();
#endif
- if (createJSValueForObjects)
+ if (createJSValueForObjectsAndSymbols)
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
return objectToVariant(e, o, visitedObjects);
}
-QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects)
+QVariant ExecutionEngine::toVariant(
+ const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols)
{
- return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr);
+ return ::toVariant(this, value, typeHint, createJSValueForObjectsAndSymbols, nullptr);
}
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects)
@@ -1720,7 +1732,8 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->get(ii);
- list << ::toVariant(e, v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects);
+ list << ::toVariant(
+ e, v, QMetaType {}, /*createJSValueForObjectsAndSymbols*/false, visitedObjects);
}
result = list;
@@ -1736,7 +1749,9 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
break;
QString key = name->toQStringNoThrow();
- map.insert(key, ::toVariant(e, val, /*type hint*/ QMetaType {}, /*createJSValueForObjects*/false, visitedObjects));
+ map.insert(key, ::toVariant(
+ e, val, /*type hint*/ QMetaType {},
+ /*createJSValueForObjectsAndSymbols*/false, visitedObjects));
}
result = map;
@@ -2428,7 +2443,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
if (a) {
*reinterpret_cast<QVariantList *>(data) = a->engine()->toVariant(
- *a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList();
+ *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false).toList();
return true;
}
break;
@@ -2442,18 +2457,20 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
break;
}
case QMetaType::QVariant:
- if (const QV4::Managed *m = value.as<QV4::Managed>())
- *reinterpret_cast<QVariant*>(data) = m->engine()->toVariant(value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false);
- else if (value.isNull())
+ if (const QV4::Managed *m = value.as<QV4::Managed>()) {
+ *reinterpret_cast<QVariant*>(data) = m->engine()->toVariant(
+ value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false);
+ } else if (value.isNull()) {
*reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr);
- else if (value.isUndefined())
+ } else if (value.isUndefined()) {
*reinterpret_cast<QVariant*>(data) = QVariant();
- else if (value.isBoolean())
+ } else if (value.isBoolean()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.booleanValue());
- else if (value.isInteger())
+ } else if (value.isInteger()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.integerValue());
- else if (value.isDouble())
+ } else if (value.isDouble()) {
*reinterpret_cast<QVariant*>(data) = QVariant(value.doubleValue());
+ }
return true;
case QMetaType::QJsonValue:
*reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 0d3b279377..d5e32ebedc 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -682,7 +682,9 @@ public:
QQmlError catchExceptionAsQmlError();
// variant conversions
- QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects = true);
+ QVariant toVariant(
+ const QV4::Value &value, QMetaType typeHint,
+ bool createJSValueForObjectsAndSymbols = true);
QV4::ReturnedValue fromVariant(const QVariant &);
QVariantMap variantMapFromJS(const QV4::Object *o);
diff --git a/src/qml/jsruntime/qv4identifierhash.cpp b/src/qml/jsruntime/qv4identifierhash.cpp
index d349b7c4fe..e974c2c0ad 100644
--- a/src/qml/jsruntime/qv4identifierhash.cpp
+++ b/src/qml/jsruntime/qv4identifierhash.cpp
@@ -127,7 +127,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
if (!d)
return nullptr;
- PropertyKey id = d->identifierTable->asPropertyKey(str);
+ PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
return lookup(id);
}
@@ -146,7 +146,7 @@ inline
const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->asPropertyKey(str);
+ return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
}
inline
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 7c5bc3188c..f6d10a575f 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -285,12 +285,17 @@ void IdentifierTable::sweep()
size -= freed;
}
-PropertyKey IdentifierTable::asPropertyKey(const QString &s)
+PropertyKey IdentifierTable::asPropertyKey(const QString &s,
+ IdentifierTable::KeyConversionBehavior conversionBehvior)
{
uint subtype;
- const uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
- if (subtype == Heap::String::StringType_ArrayIndex)
- return PropertyKey::fromArrayIndex(hash);
+ uint hash = String::createHashValue(s.constData(), s.size(), &subtype);
+ if (subtype == Heap::String::StringType_ArrayIndex) {
+ if (Q_UNLIKELY(conversionBehvior == ForceConversionToId))
+ hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype);
+ else
+ return PropertyKey::fromArrayIndex(hash);
+ }
return resolveStringEntry(s, hash, subtype)->identifier;
}
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index b2a9bc1195..c608e5d89e 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -91,7 +91,8 @@ public:
return asPropertyKey(str->d());
}
- PropertyKey asPropertyKey(const QString &s);
+ enum KeyConversionBehavior { Default, ForceConversionToId };
+ PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default);
PropertyKey asPropertyKey(const char *s, int len);
PropertyKey asPropertyKeyImpl(const Heap::String *str);
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 21a91342fb..9f3c2af174 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -294,8 +294,12 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
bool hasProp = false;
QQmlPropertyData *propertyData = nullptr;
- QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
- name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
+
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, scopeObject));
+ QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(
+ v4, context, wrapper->d(), scopeObject, name,
+ QV4::QObjectWrapper::CheckRevision, &hasProp,
+ &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
@@ -321,9 +325,10 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
if (QObject *contextObject = context->contextObject()) {
bool hasProp = false;
QQmlPropertyData *propertyData = nullptr;
- result = QV4::QObjectWrapper::getQmlProperty(v4, context, contextObject,
- name, QV4::QObjectWrapper::CheckRevision,
- &hasProp, &propertyData);
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, contextObject));
+ result = QV4::QObjectWrapper::getQmlProperty(
+ v4, context, wrapper->d(), contextObject, name,
+ QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
@@ -682,8 +687,9 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec
// Search context object
if (QObject *contextObject = context->contextObject()) {
bool hasProp = false;
+ QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
result = QV4::QObjectWrapper::getQmlProperty(
- engine, context, contextObject, name,
+ engine, context, wrapper->d(), contextObject, name,
QV4::QObjectWrapper::CheckRevision, &hasProp);
if (hasProp) {
if (base)
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 8be952571a..91bace3b01 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -248,7 +248,9 @@ QQmlPropertyData *QObjectWrapper::findProperty(
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
+ReturnedValue QObjectWrapper::getProperty(
+ ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
+ QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, property->coreIndex());
@@ -262,13 +264,13 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
ScopedContext global(scope, engine->qmlContext());
if (!global)
global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QV4::QObjectMethod::create(global, wrapper, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex());
+ return QV4::QObjectMethod::create(global, wrapper, property->coreIndex());
}
}
@@ -287,7 +289,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
-static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+static OptionalReturnedValue getDestroyOrToStringMethod(
+ ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
{
int index = 0;
if (name->equals(v4->id_destroy()))
@@ -345,7 +348,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
ExecutionEngine *v4 = engine();
- if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue;
QQmlPropertyData local;
@@ -373,13 +376,13 @@ ReturnedValue QObjectWrapper::getQmlProperty(
if (hasProperty)
*hasProperty = true;
- return getProperty(v4, d()->object(), result);
+ return getProperty(v4, d(), d()->object(), result);
}
ReturnedValue QObjectWrapper::getQmlProperty(
QV4::ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
- QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty,
- QQmlPropertyData **property)
+ Heap::Object *wrapper, QObject *object, String *name,
+ QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@@ -387,7 +390,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
return QV4::Encode::null();
}
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty))
return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
@@ -409,7 +412,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
if (property && result != &local)
*property = result;
- return getProperty(engine, object, result);
+ return getProperty(engine, wrapper, object, result);
} else {
// Check if this object is already wrapped.
if (!ddata || (ddata->jsWrapper.isUndefined() &&
@@ -429,13 +432,13 @@ ReturnedValue QObjectWrapper::getQmlProperty(
Q_ASSERT(ddata);
QV4::Scope scope(engine);
- QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object));
- if (!wrapper) {
+ QV4::Scoped<QObjectWrapper> rewrapped(scope, wrap(engine, object));
+ if (!rewrapped) {
if (hasProperty)
*hasProperty = false;
return QV4::Encode::null();
}
- return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
+ return rewrapped->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
}
@@ -867,7 +870,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
if (pd) {
QQmlPropertyData local;
local.load(property);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local);
}
return propName->toPropertyKey();
}
@@ -892,7 +895,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
if (pd) {
QQmlPropertyData local;
local.load(method);
- pd->value = that->getProperty(thatEngine, thatObject, &local);
+ pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local);
}
return methodName->toPropertyKey();
}
@@ -925,14 +928,14 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
- if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d()))
return *methodValue;
QQmlData *ddata = QQmlData::get(qobj, false);
if (!ddata || !ddata->propertyCache) {
QQmlPropertyData local;
QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, &local);
- return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined();
+ return property ? getProperty(engine, This->d(), qobj, property) : QV4::Encode::undefined();
}
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
@@ -1246,6 +1249,8 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (ddata && ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
ddata->ownContext->emitDestruction();
+ if (ddata->ownContext->contextObject() == h->object())
+ ddata->ownContext->setContextObject(nullptr);
ddata->ownContext = nullptr;
ddata->context = nullptr;
}
@@ -1884,12 +1889,24 @@ bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, c
} else if (callType == QMetaType::QObjectStar) {
qobjectPtr = nullptr;
type = callType;
- if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
+ if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) {
qobjectPtr = qobjectWrapper->object();
- else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>())
- queryEngine = qmlTypeWrapper->isSingleton();
- else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr
+ } else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>()) {
+ if (qmlTypeWrapper->isSingleton()) {
+ queryEngine = true;
+ } else if (QObject *obj = qmlTypeWrapper->object()) {
+ // attached object case
+ qobjectPtr = obj;
+ return true;
+ } else {
+ // If this is a plain type wrapper without an instance,
+ // then we got a namespace, and that's a type error
+ type = QMetaType::UnknownType;
+ return false;
+ }
+ } else if (!value.isNull() && !value.isUndefined()) { // null and undefined are nullptr
return false;
+ }
} else if (callType == qMetaTypeId<QVariant>()) {
qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, QMetaType {}));
type = callType;
@@ -2069,35 +2086,49 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
}
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setObject(object);
-
- method->d()->index = index;
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, wrapper, index));
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
+ Scoped<QObjectMethod> method(
+ valueScope,
+ valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, valueType, index));
return method.asReturnedValue();
}
-void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
+void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex)
{
Heap::FunctionObject::init(scope);
+ wrapper.set(internalClass->engine, object);
+ index = methodIndex;
}
const QMetaObject *Heap::QObjectMethod::metaObject()
{
- if (valueTypeWrapper)
- return valueTypeWrapper->metaObject();
- return object()->metaObject();
+ Scope scope(internalClass->engine);
+ if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
+ return valueWrapper->metaObject();
+ if (QObject *self = object())
+ return self->metaObject();
+ return nullptr;
+}
+
+QObject *Heap::QObjectMethod::object() const
+{
+ Scope scope(internalClass->engine);
+ if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
+ return objectWrapper->object();
+ if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
+ return typeWrapper->object();
+ return nullptr;
}
void Heap::QObjectMethod::ensureMethodsCache()
@@ -2193,14 +2224,19 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
d()->ensureMethodsCache();
Scope scope(v4);
- QQmlObjectOrGadget object(d()->object());
-
- if (!d()->object()) {
- if (!d()->valueTypeWrapper)
- return Encode::undefined();
-
- object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr());
- }
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
+ QQmlObjectOrGadget object = [&]() {
+ QV4::Scope scope(v4);
+ if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
+ return QQmlObjectOrGadget(qobject->object());
+ if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
+ return QQmlObjectOrGadget(type->object());
+ if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
+ valueWrapper = value->d();
+ return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
+ }
+ Q_UNREACHABLE();
+ }();
JSCallData cData(thisObject, argv, argc);
CallData *callData = cData.callData(scope);
@@ -2211,8 +2247,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
// The method might change the value.
const auto doCall = [&](const auto &call) {
if (!method->isConstant()) {
- QV4::Scoped<QQmlValueTypeReference> valueTypeReference(
- scope, d()->valueTypeWrapper.get());
+ QV4::Scoped<QQmlValueTypeReference> valueTypeReference(scope, valueWrapper);
if (valueTypeReference) {
QV4::ScopedValue rv(scope, call());
valueTypeReference->d()->writeBack();
@@ -2335,9 +2370,11 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) {
object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance);
}
- Scoped<QMetaObjectWrapper> metaObject(scope, this);
- object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ if (object) {
+ Scoped<QMetaObjectWrapper> metaObject(scope, this);
+ object->defineDefaultProperty(v4->id_constructor(), metaObject);
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
+ }
return object.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index f7bdda5f6f..ca0e5758c4 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -96,32 +96,29 @@ private:
};
#define QObjectMethodMembers(class, Member) \
- Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \
+ Member(class, Pointer, Object *, wrapper) \
Member(class, NoMark, QV4QPointer<QObject>, qObj) \
Member(class, NoMark, int, index)
-DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
- DECLARE_MARKOBJECTS(QObjectMethod);
+DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) {
+ DECLARE_MARKOBJECTS(QObjectMethod)
QQmlPropertyData *methods;
int methodCount;
alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
- void init(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope, Object *wrapper, int index);
void destroy()
{
if (methods != reinterpret_cast<QQmlPropertyData *>(&_singleMethod))
delete[] methods;
- qObj.destroy();
FunctionObject::destroy();
}
void ensureMethodsCache();
const QMetaObject *metaObject();
- QObject *object() const { return qObj.data(); }
- void setObject(QObject *o) { qObj = o; }
-
+ QObject *object() const;
};
struct QMetaObjectWrapper : FunctionObject {
@@ -160,6 +157,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static void initializeBindings(ExecutionEngine *engine);
+ const QMetaObject *metaObject() const
+ {
+ if (QObject *o = object())
+ return o->metaObject();
+ return nullptr;
+ }
+
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(
@@ -169,8 +173,8 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
\
static ReturnedValue getQmlProperty(
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
- QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr,
- QQmlPropertyData **property = nullptr);
+ Heap::Object *wrapper, QObject *object, String *name, RevisionMode revisionMode,
+ bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(
ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
@@ -186,7 +190,9 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall);
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
+ static ReturnedValue getProperty(
+ ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
+ QQmlPropertyData *property);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -243,7 +249,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE
if (!o || o->internalClass != lookup->qobjectLookup.ic)
return revertLookup();
- const Heap::QObjectWrapper *This = static_cast<const Heap::QObjectWrapper *>(o);
+ Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o);
QObject *qobj = This->object();
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
@@ -271,7 +277,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE
return revertLookup();
}
- return getProperty(engine, qobj, property);
+ return getProperty(engine, This, qobj, property);
}
struct QQmlValueTypeWrapper;
@@ -283,7 +289,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
- static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index);
static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index f27754f4a3..98300b1dd0 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1544,6 +1544,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
if (done->booleanValue())
break;
++argCount;
+ constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements
+ if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) {
+ scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator"));
+ return { nullptr, 0 };
+ }
v = scope.alloc<Scope::Uninitialized>();
}
}
@@ -1608,7 +1613,7 @@ ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine
return checkedResult(engine, fo.call(&thisObject, argv, argc));
}
- memcpy(frame->jsFrame->args, argv, argc * sizeof(Value));
+ memmove(frame->jsFrame->args, argv, argc * sizeof(Value));
frame->init(fo.function(), frame->jsFrame->argValues<Value>(), argc,
frame->callerCanHandleTailCall());
frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject,
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index a55f72e241..69584c114e 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -230,6 +230,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
return calculateHashValue(ch, end, subtype);
}
+ static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype)
+ {
+ const QChar *end = ch + length;
+ return calculateHashValue<String::DisallowArrayIndex>(ch, end, subtype);
+ }
+
static uint createHashValue(const char *ch, int length, uint *subtype)
{
const char *end = ch + length;
@@ -243,15 +249,19 @@ protected:
static qint64 virtualGetLength(const Managed *m);
public:
- template <typename T>
+ enum IndicesBehavior {Default, DisallowArrayIndex};
+ template <IndicesBehavior Behavior = Default, typename T>
static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
{
// array indices get their number as hash value
- uint h = stringToArrayIndex(ch, end);
- if (h != UINT_MAX) {
- if (subtype)
- *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
- return h;
+ uint h = UINT_MAX;
+ if constexpr (Behavior != DisallowArrayIndex) {
+ h = stringToArrayIndex(ch, end);
+ if (h != UINT_MAX) {
+ if (subtype)
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
+ return h;
+ }
}
while (ch < end) {
diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h
index 3ba488d586..76381324aa 100644
--- a/src/qml/qml/ftw/qrecyclepool_p.h
+++ b/src/qml/qml/ftw/qrecyclepool_p.h
@@ -132,8 +132,7 @@ template<typename T, int Step>
T *QRecyclePool<T, Step>::New()
{
T *rv = d->allocate();
- new (rv) T;
- return rv;
+ return new (rv) T;
}
template<typename T, int Step>
@@ -141,8 +140,7 @@ template<typename T1>
T *QRecyclePool<T, Step>::New(const T1 &a)
{
T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return new (rv) T(a);
}
template<typename T, int Step>
@@ -150,8 +148,7 @@ template<typename T1>
T *QRecyclePool<T, Step>::New(T1 &a)
{
T *rv = d->allocate();
- new (rv) T(a);
- return rv;
+ return new (rv) T(a);
}
template<typename T, int Step>
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 27088e1032..87d61f5839 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -155,24 +155,24 @@ public:
};
struct NotifyList {
- quint64 connectionMask;
-
- quint16 maximumTodoIndex;
- quint16 notifiesSize;
-
- QQmlNotifierEndpoint *todo;
- QQmlNotifierEndpoint**notifies;
+ QAtomicInteger<quint64> connectionMask;
+ QQmlNotifierEndpoint *todo = nullptr;
+ QQmlNotifierEndpoint**notifies = nullptr;
+ quint16 maximumTodoIndex = 0;
+ quint16 notifiesSize = 0;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
- NotifyList *notifyList;
+ QAtomicPointer<NotifyList> notifyList;
- inline QQmlNotifierEndpoint *notify(int index);
+ inline QQmlNotifierEndpoint *notify(int index) const;
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
- void disconnectNotifiers();
+
+ enum class DeleteNotifyList { Yes, No };
+ void disconnectNotifiers(DeleteNotifyList doDelete);
// The context that created the C++ object; not refcounted to prevent cycles
QQmlContextData *context = nullptr;
@@ -326,23 +326,31 @@ bool QQmlData::wasDeleted(const QObject *object)
return ddata && ddata->isQueuedForDeletion;
}
-QQmlNotifierEndpoint *QQmlData::notify(int index)
+inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
+{
+ return connectionMask & (1ULL << quint64(index % 64));
+}
+
+QQmlNotifierEndpoint *QQmlData::notify(int index) const
{
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
Q_ASSERT(index <= 0xFFFF);
- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
+ NotifyList *list = notifyList.loadRelaxed();
+ if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
return nullptr;
- } else if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else if (index <= notifyList->maximumTodoIndex) {
- notifyList->layout();
- }
- if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else {
- return nullptr;
+ if (index < list->notifiesSize)
+ return list->notifies[index];
+
+ if (index <= list->maximumTodoIndex) {
+ list->layout();
+ if (index < list->notifiesSize)
+ return list->notifies[index];
}
+
+ return nullptr;
}
/*
@@ -351,7 +359,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index)
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
+ // This can be called from any thread.
+ // We still use relaxed semantics. If we're on a thread different from the "home" thread
+ // of the QQmlData, two interesting things might happen:
+ //
+ // 1. The list might go away while we hold it. In that case we are dealing with an object whose
+ // QObject dtor is being executed concurrently. This is UB already without the notify lists.
+ // Therefore, we don't need to consider it.
+ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
+ // we "misreport" the endpoint. Since ordering of events across threads is inherently
+ // nondeterministic, either result is correct in that case. We can accept it.
+
+ NotifyList *list = notifyList.loadRelaxed();
+ return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
}
bool QQmlData::hasBindingBit(int coreIndex) const
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index ff8e2de238..e14931ddba 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -529,7 +529,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
// Disconnect the notifiers now - during object destruction this would be too late, since
// the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to
// get the metaobject anymore.
- d->disconnectNotifiers();
+ d->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
}
}
@@ -590,7 +590,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (!ddata->notifyList)
+
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
return;
auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
@@ -1447,49 +1450,73 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask.storeRelaxed(
+ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64)));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask.storeRelaxed(0);
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1573,7 +1600,7 @@ void QQmlData::destroyed(QObject *object)
guard->objectDestroyed(object);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 77ea35d034..15315a9000 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -130,9 +130,16 @@ public:
};
struct QPropertyChangeTrigger : QPropertyObserver {
- QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger), m_expression(expression) {}
- QQmlJavaScriptExpression * m_expression;
- QObject *target = nullptr;
+ Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger)
+
+ QPropertyChangeTrigger(QQmlJavaScriptExpression *expression)
+ : QPropertyObserver(&QPropertyChangeTrigger::trigger)
+ , m_expression(expression)
+ {
+ }
+
+ QPointer<QObject> target;
+ QQmlJavaScriptExpression *m_expression;
int propertyIndex = 0;
static void trigger(QPropertyObserver *, QUntypedPropertyData *);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index d04d6751cd..e33d942833 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -1519,6 +1519,15 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
// The url for the path containing files for this import
QString url = resolveLocalUrl(base, uri);
+ if (url.isEmpty()) {
+ QQmlError error;
+ error.setDescription(
+ QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri));
+ error.setUrl(baseUrl);
+ errors->prepend(error);
+ return QTypeRevision();
+ }
+
if (!url.endsWith(Slash) && !url.endsWith(Backslash))
url += Slash;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index f937a3fff8..d156862ea4 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -390,19 +390,35 @@ void QQmlPropertyCapture::captureProperty(
captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
}
+bool QQmlJavaScriptExpression::needsPropertyChangeTrigger(QObject *target, int propertyIndex)
+{
+ TriggerList **prev = &qpropertyChangeTriggers;
+ TriggerList *current = qpropertyChangeTriggers;
+ while (current) {
+ if (!current->target) {
+ *prev = current->next;
+ QRecyclePool<TriggerList>::Delete(current);
+ current = *prev;
+ } else if (current->target == target && current->propertyIndex == propertyIndex) {
+ return false; // already installed
+ } else {
+ prev = &current->next;
+ current = current->next;
+ }
+ }
+
+ return true;
+}
+
void QQmlPropertyCapture::captureTranslation()
{
// use a unique invalid index to avoid needlessly querying the metaobject for
// the correct index of of the translationLanguage property
int const invalidIndex = -2;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == engine && trigger->propertyIndex == invalidIndex)
- return; // already installed
+ if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
+ trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
- auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
-
- trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
}
void QQmlPropertyCapture::captureBindableProperty(
@@ -412,16 +428,14 @@ void QQmlPropertyCapture::captureBindableProperty(
// the automatic capturing process already takes care of everything
if (!expression->mustCaptureBindableProperty())
return;
- for (auto trigger = expression->qpropertyChangeTriggers; trigger;
- trigger = trigger->next) {
- if (trigger->target == o && trigger->propertyIndex == c)
- return; // already installed
+
+ if (expression->needsPropertyChangeTrigger(o, c)) {
+ auto trigger = expression->allocatePropertyChangeTrigger(o, c);
+ QUntypedBindable bindable;
+ void *argv[] = { &bindable };
+ metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
+ bindable.observe(trigger);
}
- auto trigger = expression->allocatePropertyChangeTrigger(o, c);
- QUntypedBindable bindable;
- void *argv[] = { &bindable };
- metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
- bindable.observe(trigger);
}
void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify)
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index bfdc922729..4edfeda633 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -166,6 +166,8 @@ public:
QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; }
bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); }
+
+ bool needsPropertyChangeTrigger(QObject *target, int propertyIndex);
QPropertyChangeTrigger *allocatePropertyChangeTrigger(QObject *target, int propertyIndex);
protected:
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index fdf74f4873..80a7ca1e1a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -282,7 +282,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
- // Clone Q_ENUMS
+ // Clone enums registered with the metatype system
for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
QMetaEnum enumerator = mo->enumerator(ii);
diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp
index 0cc544e266..51dee8fa5f 100644
--- a/src/qml/qml/qqmlpropertybinding.cpp
+++ b/src/qml/qml/qqmlpropertybinding.cpp
@@ -163,7 +163,8 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP
void QQmlPropertyBindingJS::expressionChanged()
{
- if (!asBinding()->propertyDataPtr)
+ auto binding = asBinding();
+ if (!binding->propertyDataPtr)
return;
if (QQmlData::wasDeleted(asBinding()->target()))
return;
@@ -185,8 +186,9 @@ void QQmlPropertyBindingJS::expressionChanged()
return;
}
m_error.setTag(InEvaluationLoop);
- asBinding()->evaluateRecursive();
- asBinding()->notifyRecursive();
+ PendingBindingObserverList bindingObservers;
+ binding->evaluateRecursive(bindingObservers);
+ binding->notifyNonRecursive(bindingObservers);
m_error.setTag(NoTag);
}
@@ -263,10 +265,17 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
setIsUndefined(true);
//suspend binding evaluation state for reset and subsequent read
auto state = QtPrivate::suspendCurrentBindingStatus();
- prop.reset();
+ prop.reset(); // May re-allocate the bindingData
QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr);
QtPrivate::restoreBindingStatus(state);
writeBackCurrentValue(std::move(currentValue));
+
+ // Re-fetch binding data
+ bindingData = storage->bindingData(propertyDataPtr);
+ if (!bindingData)
+ bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType());
+ bindingDataPointer = QPropertyBindingDataPointer {bindingData};
+
// reattach the binding (without causing a new notification)
if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) {
qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n"
@@ -278,7 +287,7 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void
firstObserver = bindingDataPointer.firstObserver();
bindingData->d_ref() = reinterpret_cast<quintptr>(this) | QtPrivate::QPropertyBindingData::BindingBit;
if (firstObserver)
- bindingDataPointer.setObservers(firstObserver.ptr);
+ prependObserver(firstObserver);
} else {
QQmlError qmlError;
auto location = jsExpression()->sourceLocation();
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 0580af56f0..93736b0002 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -85,19 +85,31 @@ bool QQmlTypeWrapper::isSingleton() const
return d()->type().isSingleton();
}
+const QMetaObject *QQmlTypeWrapper::metaObject() const
+{
+ const QQmlType type = d()->type();
+ if (!type.isValid())
+ return nullptr;
+
+ if (type.isSingleton())
+ return type.metaObject();
+
+ return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine()));
+}
+
QObject *QQmlTypeWrapper::object() const
{
const QQmlType type = d()->type();
if (!type.isValid())
return nullptr;
- QQmlEngine *qmlEngine = engine()->qmlEngine();
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
if (type.isSingleton())
- return QQmlEnginePrivate::get(qmlEngine)->singletonInstance<QObject *>(type);
+ return qmlEngine->singletonInstance<QObject *>(type);
return qmlAttachedPropertiesObject(
d()->object,
- type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine)));
+ type.attachedPropertiesFunction(qmlEngine));
}
QObject* QQmlTypeWrapper::singletonObject() const
@@ -226,7 +238,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
// check for property.
bool ok;
- const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok);
+ const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(
+ v4, context, w->d(), qobjectSingleton, name,
+ QV4::QObjectWrapper::IgnoreRevision, &ok);
if (hasProperty)
*hasProperty = ok;
@@ -267,8 +281,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
QObject *ao = qmlAttachedPropertiesObject(
object,
type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine())));
- if (ao)
- return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty);
+ if (ao) {
+ return QV4::QObjectWrapper::getQmlProperty(
+ v4, context, w->d(), ao, name, QV4::QObjectWrapper::IgnoreRevision,
+ hasProperty);
+ }
// Fall through to base implementation
}
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 71e9ee04eb..56d50bbf83 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -102,6 +102,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
V4_NEEDS_DESTROY
bool isSingleton() const;
+ const QMetaObject *metaObject() const;
QObject *object() const;
QObject *singletonObject() const;
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index ed58157c26..a473fc5458 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -163,6 +163,7 @@ public:
int typeId() const;
QMetaType type() const;
bool write(QObject *target, int propertyIndex) const;
+ const QMetaObject *metaObject() const { return d()->metaObject(); }
QQmlPropertyData dataForPropertyKey(PropertyKey id) const;
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index a9ee30de49..9ccdf42587 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -403,7 +403,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
QStringLiteral("jsroot.qmltypes") };
const auto importBuiltins = [&](const QStringList &imports) {
for (auto const &dir : imports) {
- QDirIterator it { dir, qmltypesFiles, QDir::NoFilter, QDirIterator::Subdirectories };
+ QDirIterator it { dir, qmltypesFiles, QDir::NoFilter };
while (it.hasNext() && !qmltypesFiles.isEmpty()) {
readQmltypes(it.next(), &result.objects, &result.dependencies);
qmltypesFiles.removeOne(it.fileName());
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index c817cd61be..ee55fa214a 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -70,21 +70,35 @@ static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get())
template<typename QQmlJSScopePtr, typename Action>
static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
{
+ if (!type)
+ return false;
+
// NB: among other things, getQQmlJSScopeFromSmartPtr() also resolves const
// vs non-const pointer issue, so use it's return value as the type
using T = decltype(
getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(std::declval<QQmlJSScope::ConstPtr>()));
+ const bool isValueType = (type->accessSemantics() == QQmlJSScope::AccessSemantics::Value);
+
QDuplicateTracker<T> seen;
for (T scope = type; scope && !seen.hasSeen(scope);
scope = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->baseType())) {
- // Extensions override their base types
- for (T extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->extensionType());
- extension && !seen.hasSeen(extension);
- extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType())) {
+ QDuplicateTracker<T> seenExtensions;
+ // Extensions override the types they extend. However, usually base
+ // types of extensions are ignored. The unusual cases are when we
+ // have a value type or when we have the QObject type, in which case
+ // we also study the extension's base type hierarchy.
+ const bool isQObject = scope->internalName() == QLatin1String("QObject");
+ T extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->extensionType());
+ do {
+ if (!extension || seenExtensions.hasSeen(extension))
+ break;
+
if (check(extension))
return true;
- }
+
+ extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType());
+ } while (isValueType || isQObject);
if (check(scope))
return true;
diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp
index ee48a71c29..a2bbb25581 100644
--- a/src/qmldom/qqmldomreformatter.cpp
+++ b/src/qmldom/qqmldomreformatter.cpp
@@ -585,16 +585,27 @@ protected:
return false;
}
+
+ void outputScope(VariableScope scope) {
+ switch (scope) {
+ case VariableScope::Const:
+ out("const ");
+ break;
+ case VariableScope::Let:
+ out("let ");
+ break;
+ case VariableScope::Var:
+ out("var ");
+ break;
+ default:
+ break;
+ }
+ }
+
bool visit(PatternElement *ast) override
{
if (ast->isForDeclaration) {
- if (ast->scope == VariableScope::Var) {
- out("var ");
- } else if (ast->scope == VariableScope::Let) {
- out("let ");
- } else if (ast->scope == VariableScope::Const) {
- out("const ");
- }
+ outputScope(ast->scope);
}
accept(ast->bindingTarget);
switch (ast->type) {
@@ -678,7 +689,7 @@ protected:
if (ast->initialiser) {
accept(ast->initialiser);
} else if (ast->declarations) {
- out("var ");
+ outputScope(ast->declarations->declaration->scope);
accept(ast->declarations);
}
out("; "); // ast->firstSemicolonToken
@@ -867,12 +878,15 @@ protected:
out(ast->identifierToken);
}
out(ast->lparenToken);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
+ const bool needParentheses = ast->formals &&
+ (ast->formals->next ||
+ (ast->formals->element && ast->formals->element->bindingTarget));
+ if (ast->isArrowFunction && needParentheses)
out("(");
int baseIndent = lw.increaseIndent(1);
accept(ast->formals);
lw.decreaseIndent(1, baseIndent);
- if (ast->isArrowFunction && ast->formals && ast->formals->next)
+ if (ast->isArrowFunction && needParentheses)
out(")");
out(ast->rparenToken);
if (ast->isArrowFunction && !ast->formals)
@@ -963,7 +977,11 @@ protected:
bool visit(FormalParameterList *ast) override
{
for (FormalParameterList *it = ast; it; it = it->next) {
- out(it->element->bindingIdentifier.toString()); // TODO
+ // compare FormalParameterList::finish
+ if (auto id = it->element->bindingIdentifier.toString(); !id.startsWith(u"arg#"))
+ out(id);
+ if (it->element->bindingTarget)
+ accept(it->element->bindingTarget);
if (it->next)
out(", ");
}
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index 377a4c1e6f..d3cf23b490 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -1259,6 +1259,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
addCacheItem(cacheItem, it);
reuseItem(cacheItem, index, flags);
cacheItem->referenceObject();
+
+ if (index == m_compositor.count(group) - 1)
+ requestMoreIfNecessary();
return cacheItem->object;
}
diff --git a/src/quick/doc/images/qml-item-canvas-startAngle.png b/src/quick/doc/images/qml-item-canvas-startAngle.png
index bf82c3aa4b..7930284896 100644
--- a/src/quick/doc/images/qml-item-canvas-startAngle.png
+++ b/src/quick/doc/images/qml-item-canvas-startAngle.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/images/qt-logo.png b/src/quick/doc/snippets/qml/images/qt-logo.png
new file mode 100644
index 0000000000..30c621c9c6
--- /dev/null
+++ b/src/quick/doc/snippets/qml/images/qt-logo.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/item/itemGrab.qml
index e26e3dc55e..78fc53833b 100644
--- a/src/quick/doc/snippets/qml/itemGrab.qml
+++ b/src/quick/doc/snippets/qml/item/itemGrab.qml
@@ -54,44 +54,33 @@ Item {
width: 320
height: 480
-//! [grab-source]
+//! [grab-to-file]
Rectangle {
- id: source
+ id: sourceRectangle
width: 100
height: 100
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
+
+ Component.onCompleted: {
+ sourceRectangle.grabToImage(function(result) {
+ result.saveToFile("something.png")
+ })
+ }
}
-//! [grab-source]
+//! [grab-to-file]
-//! [grab-image-target]
+//! [grab-to-image]
Image {
id: image
}
-//! [grab-image-target]
- Timer {
- repeat: false
- running: true
- interval: 1000
- onTriggered: {
-//! [grab-to-file]
- // ...
- source.grabToImage(function(result) {
- result.saveToFile("something.png");
- });
-//! [grab-to-file]
-
-//! [grab-to-cache]
-
- // ...
- source.grabToImage(function(result) {
- image.source = result.url;
- },
- Qt.size(50, 50));
-//! [grab-to-cache]
- }
- }
+Component.onCompleted: {
+ sourceRectangle.grabToImage(function(result) {
+ image.source = result.url
+ }, Qt.size(50, 50))
+}
+//! [grab-to-image]
}
diff --git a/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
new file mode 100644
index 0000000000..1946ee4af0
--- /dev/null
+++ b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+//![0]
+Window {
+ // visible is false by default
+ Window {
+ transientParent: null
+ visible: true
+ }
+//![0]
+
+ id: outer
+ Timer {
+ interval: 2000
+ running: true
+ onTriggered: outer.visible = true
+ }
+//![1]
+}
+//![1]
diff --git a/src/quick/doc/snippets/qml/splashWindow.qml b/src/quick/doc/snippets/qml/splashWindow.qml
new file mode 100644
index 0000000000..17d4c674e3
--- /dev/null
+++ b/src/quick/doc/snippets/qml/splashWindow.qml
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [entire]
+import QtQuick
+
+Window {
+ id: mainWindow
+ title: "Main Window"
+ color: "#456"
+ property real defaultSpacing: 10
+
+ property Splash splash: Splash {
+ onTimeout: mainWindow.show()
+ }
+
+ component Splash: Window {
+ id: splash
+
+ // a splash screen has no titlebar
+ flags: Qt.SplashScreen
+ // the transparent color lets background behind the image edges show through
+ color: "transparent"
+ modality: Qt.ApplicationModal // in case another application window is showing
+ title: "Splash Window" // for the taskbar/dock, task switcher etc.
+ visible: true
+
+ // here we use the Screen attached property to center the splash window
+ //! [screen-properties]
+ x: (Screen.width - splashImage.width) / 2
+ y: (Screen.height - splashImage.height) / 2
+ //! [screen-properties]
+ width: splashImage.width
+ height: splashImage.height
+
+ property int timeoutInterval: 2000
+ signal timeout
+
+ Image {
+ id: splashImage
+ source: "images/qt-logo.png"
+ }
+
+ TapHandler {
+ onTapped: splash.timeout()
+ }
+
+ Timer {
+ interval: splash.timeoutInterval; running: true; repeat: false
+ onTriggered: {
+ splash.visible = false
+ splash.timeout()
+ }
+ }
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/snippets/qml/windowActiveAttached.qml b/src/quick/doc/snippets/qml/windowActiveAttached.qml
new file mode 100644
index 0000000000..9d2ddc2b99
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowActiveAttached.qml
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+
+Text {
+ text: Window.active ? "active" : "inactive"
+}
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowPalette.qml b/src/quick/doc/snippets/qml/windowPalette.qml
new file mode 100644
index 0000000000..c566d61dbb
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowPalette.qml
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+
+//![declaration-and-color]
+Window {
+ visible: true
+
+ // here we use the Window.active and Window.palette ordinary properties
+ color: active ? palette.active.window : palette.inactive.window
+//![declaration-and-color]
+
+ // colors that are not customized here come from SystemPalette
+ palette.active.window: "peachpuff"
+ palette.windowText: "brown"
+
+ Text {
+ anchors.centerIn: parent
+ // here we use the Window.active attached property and the Item.palette property
+ color: Window.active ? palette.active.windowText : palette.inactive.windowText
+ text: Window.active ? "active" : "inactive"
+ }
+
+ Button {
+ text: "Button"
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 6
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+//![closing-brace]
+}
+//![closing-brace]
+//![entire]
diff --git a/src/quick/doc/snippets/qml/windowVisibility.qml b/src/quick/doc/snippets/qml/windowVisibility.qml
new file mode 100644
index 0000000000..b5f89a3894
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowVisibility.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [entire]
+import QtQuick
+import QtQuick.Controls
+
+Window {
+ id: win
+ flags: Qt.Window | Qt.WindowFullscreenButtonHint
+ visibility: fullscreenButton.checked ? Window.FullScreen : Window.Windowed
+
+ Button {
+ id: fullscreenButton
+ anchors {
+ right: parent.right
+ top: parent.top
+ margins: 6
+ }
+ width: height
+ checkable: true
+ Binding on checked { value: win.visibility === Window.FullScreen }
+ text: "â›ļ"
+ ToolTip.visible: hovered
+ ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
+ ToolTip.text: win.visibility === Window.FullScreen ? qsTr("restore") : qsTr("fill screen")
+ }
+}
+//! [entire]
diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
index 574dfcb58f..da96d31258 100644
--- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
+++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
@@ -171,6 +171,16 @@ This will append the following declaration to the translation file, where you ca
</context>
\endcode
+Next, add the following bindings to the root QML component of your application:
+\code
+LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
+LayoutMirroring.childrenInherit: true
+\endcode
+
+The first binding ensures that the UI will be mirrored appropriately when a
+right-to-left locale is set. The second binding ensures that child items of the
+root component will also respect mirroring.
+
You can test that the layout direction works as expected by running your Qt Quick application with
the compiled translation file:
diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc
index b0cfa247ec..2f6c87e840 100644
--- a/src/quick/doc/src/examples.qdoc
+++ b/src/quick/doc/src/examples.qdoc
@@ -123,7 +123,6 @@ Creator.
\b{QML Types and Controls}
\list
\li \l{Qt Quick Controls - Gallery}{Controls Gallery}
- \li \l{Calendar Example}
\li \l{tableview/gameoflife}{TableView}
\li \l{Qt Quick Examples - Text}{Text and Fonts}
\li \l{Qt Quick Examples - Toggle Switch}{Custom Toggle Switch}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 7503e3ada9..ea1a2372db 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -2404,8 +2404,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::Func
\image qml-item-canvas-startAngle.png
- The \a anticlockwise parameter is \c true for each arc in the figure above
- because they are all drawn in the anticlockwise direction.
+ The \a anticlockwise parameter is \c false for each arc in the figure above
+ because they are all drawn in the clockwise direction.
\sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
Context Standard for arc()}
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 556ccd8e0f..39a0d6659a 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1393,10 +1393,14 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
const QVector2D velocity = firstPointLocalVelocity(event);
bool overThreshold = false;
- if (q->yflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
- if (q->xflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ if (event->pointCount() == 1) {
+ if (q->yflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
+ if (q->xflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ } else {
+ qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event;
+ }
drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
@@ -2567,11 +2571,17 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
QQuickDeliveryAgentPrivate::isTabletEvent(event)))
return false; // don't filter hover events or wheel events, for example
Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
- qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
Q_D(QQuickFlickable);
// If a touch event contains a new press point, don't steal right away: watch the movements for a while
if (isTouch && static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
d->stealMouse = false;
+ // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child
+ if (event->pointCount() > 1) {
+ qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver;
+ d->stealMouse = false;
+ } else {
+ qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
+ }
const auto &firstPoint = event->points().first();
if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) {
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index d94ccddcbc..916ae61cf5 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1673,6 +1673,7 @@ void QQuickGridView::setCellWidth(qreal cellWidth)
d->updateViewport();
emit cellWidthChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
}
}
@@ -1690,6 +1691,7 @@ void QQuickGridView::setCellHeight(qreal cellHeight)
d->updateViewport();
emit cellHeightChanged();
d->forceLayoutPolish();
+ QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
}
}
/*!
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 88f58cc0fa..52300bebe0 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -286,6 +286,7 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
\value Image.TileVertically the image is stretched horizontally and tiled vertically
\value Image.TileHorizontally the image is stretched vertically and tiled horizontally
\value Image.Pad the image is not transformed
+ \br
\table
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index 68c5f3db97..dc5750dc0a 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -381,17 +381,15 @@ QSharedPointer<QQuickItemGrabResult> QQuickItem::grabToImage(const QSize &target
*
* If the grab could not be initiated, the function returns \c false.
*
- * The following snippet shows how to grab an item and store the results to
- * a file.
+ * The following snippet shows how to grab an item and store the results in
+ * a file:
*
- * \snippet qml/itemGrab.qml grab-source
- * \snippet qml/itemGrab.qml grab-to-file
+ * \snippet qml/item/itemGrab.qml grab-to-file
*
* The following snippet shows how to grab an item and use the results in
- * another image element.
+ * another image element:
*
- * \snippet qml/itemGrab.qml grab-image-target
- * \snippet qml/itemGrab.qml grab-to-cache
+ * \snippet qml/item/itemGrab.qml grab-to-image
*
* \note This function will render the item to an offscreen surface and
* copy that surface from the GPU's memory into the CPU's memory, which can
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index 6b03d6c16b..5342adaddf 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -125,6 +125,8 @@ void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableIt
actions << QQuickStateAction(item->item, QLatin1String("x"), QVariant(to.x()));
actions << QQuickStateAction(item->item, QLatin1String("y"), QVariant(to.y()));
+ actions[0].fromValue = item->itemX();
+ actions[1].fromValue = item->itemY();
m_transitioner->runningJobs << this;
QQuickTransitionManager::transition(actions, trans, item->item);
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index a4c0a8b740..9e7a8f4803 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2213,6 +2213,10 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
\note While an item is in the pool, it might still be alive and respond
to connected signals and bindings.
+ \note For an item to be pooled, it needs to be completely flicked out of the bounds
+ of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer.}
+ Some items will also never be pooled or reused, such as \l currentItem.
+
The following example shows a delegate that animates a spinning rectangle. When
it is pooled, the animation is temporarily paused:
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index 5e95a92238..c2e3245011 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -61,8 +61,8 @@ QT_BEGIN_NAMESPACE
\ingroup qtquick-visual
\brief The QQuickPalette class contains color groups for each QML item state.
- A palette consists of three color groups: Active, Disabled, and Inactive.
- Active color group is the default group, its colors are used for other groups
+ A palette consists of three color groups: \c active, \c disabled, and \c inactive.
+ The \c active color group is the default group: its colors are used for other groups
if colors of these groups aren't explicitly specified.
In the following example, color is applied for all color groups:
@@ -104,18 +104,19 @@ QT_BEGIN_NAMESPACE
\endcode
It is also possible to specify colors like this:
- \code
- palette {
- buttonText: "azure"
- button: "khaki"
- disabled {
- buttonText: "lavender"
- button: "coral"
- }
- }
- \endcode
- This approach is convenient when you need to specify a whole palette with all color groups.
+ \snippet qtquickcontrols-custom-palette-buttons.qml palette
+
+ This approach is especially convenient when you need to specify a whole
+ palette with all color groups; but as with the other cases above, the
+ colors that are not specified are initialized from SystemPalette, or
+ potentially the \l {Styling Qt Quick Controls}{Qt Quick Controls style},
+ if one is in use.
+
+ \note Some Controls styles use some palette colors, but many styles use
+ independent colors.
+
+ \sa Window::palette, Item::palette, Popup::palette, SystemPalette
*/
/*!
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index 913003ef15..914ebd3047 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -75,6 +75,8 @@ QT_BEGIN_NAMESPACE
Note that the Screen type is not valid at Component.onCompleted, because
the Item or Window has not been displayed on a screen by this time.
+
+ \sa {Qt Quick Examples - Window and Screen}
*/
/*!
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index e35c128001..41fbcc7c48 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -396,7 +396,12 @@ QString QQuickTextEdit::text() const
remarkably better performance for modifying especially large rich text
content.
- \sa clear(), textFormat
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property.
+
+ \sa clear(), preeditText, textFormat
*/
void QQuickTextEdit::setText(const QString &text)
{
@@ -428,6 +433,11 @@ void QQuickTextEdit::setText(const QString &text)
\since 5.7
This property contains partial text input from an input method.
+
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa inputMethodHints
*/
QString QQuickTextEdit::preeditText() const
{
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 0096158d58..f41ab2f626 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -125,7 +125,13 @@ void QQuickTextInput::componentComplete()
The text in the TextInput.
- \sa clear()
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property. To get whole text displayed in the TextInput
+ use \l displayText property.
+
+ \sa clear(), displayText, preeditText
*/
QString QQuickTextInput::text() const
{
@@ -2391,7 +2397,10 @@ QString QQuickTextInput::displayText() const
This property contains partial text input from an input method.
- \sa displayText
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa displayText, inputMethodHints
*/
QString QQuickTextInput::preeditText() const
{
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 1a57fdd4f2..597cd300df 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -3269,10 +3269,12 @@ void QQuickWindow::endExternalCommands()
whether it's a dialog, popup, or a regular window, and whether it should
have a title bar, etc.
- The flags which you read from this property might differ from the ones
+ The flags that you read from this property might differ from the ones
that you set if the requested flags could not be fulfilled.
- \sa Qt::WindowFlags
+ \snippet qml/splashWindow.qml entire
+
+ \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen}
*/
/*!
@@ -3357,10 +3359,12 @@ void QQuickWindow::endExternalCommands()
visibility property you will always get the actual state, never
\c AutomaticVisibility.
- When a window is not visible its visibility is Hidden, and setting
+ When a window is not visible, its visibility is \c Hidden, and setting
visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
- \sa visible
+ \snippet qml/windowVisibility.qml entire
+
+ \sa visible, {Qt Quick Examples - Window and Screen}
\since 5.1
*/
@@ -3466,17 +3470,8 @@ void QQuickWindow::endExternalCommands()
Item or Window within which it was declared, you can remove that
relationship by setting \c transientParent to \c null:
- \qml
- import QtQuick.Window 2.13
-
- Window {
- // visible is false by default
- Window {
- transientParent: null
- visible: true
- }
- }
- \endqml
+ \snippet qml/nestedWindowTransientParent.qml 0
+ \snippet qml/nestedWindowTransientParent.qml 1
In order to cause the window to be centered above its transient parent by
default, depending on the window manager, it may also be necessary to set
@@ -3522,6 +3517,9 @@ void QQuickWindow::endExternalCommands()
The active status of the window.
+ \snippet qml/windowPalette.qml declaration-and-color
+ \snippet qml/windowPalette.qml closing-brace
+
\sa requestActivate()
*/
@@ -3535,14 +3533,7 @@ void QQuickWindow::endExternalCommands()
Here is an example which changes a label to show the active state of the
window in which it is shown:
- \qml
- import QtQuick 2.4
- import QtQuick.Window 2.2
-
- Text {
- text: Window.active ? "active" : "inactive"
- }
- \endqml
+ \snippet qml/windowActiveAttached.qml entire
*/
/*!
@@ -4150,10 +4141,11 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
palette which serves as a default for all application windows. You can also set the default palette
for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML.
- ApplicationWindow propagates explicit palette properties to child controls. If you change a specific
- property on the window's palette, that property propagates to all child controls in the window,
+ Window propagates explicit palette properties to child items and controls,
overriding any system defaults for that property.
+ \snippet qml/windowPalette.qml entire
+
\sa Item::palette, Popup::palette, ColorGroup, SystemPalette
//! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
*/
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 4ddef6b4c2..a9299ff17e 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -143,6 +143,18 @@ void QSGSoftwareRenderContext::initializeIfNeeded()
void QSGSoftwareRenderContext::invalidate()
{
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ qDeleteAll(m_fontEnginesToClean);
+ m_fontEnginesToClean.clear();
+
+ qDeleteAll(m_glyphCaches);
+ m_glyphCaches.clear();
+
m_sg->renderContextInvalidated(this);
emit invalidated();
}
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index c6877369c0..ab70d8e09f 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -3251,7 +3251,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
// unmerged batch since the material (and so the shaders) is the same.
QSGGeometry *g = gn->geometry();
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g);
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode);
if (!sms)
return false;
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 8dbfcad38b..b8f0f336f2 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -292,7 +292,7 @@ void QSGSimpleTextureNode::setTextureCoordinatesTransform(QSGSimpleTextureNode::
return;
d->texCoordMode = mode;
qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->sourceRect, d->texCoordMode);
- markDirty(DirtyMaterial);
+ markDirty(DirtyGeometry | DirtyMaterial);
}
/*!
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 63573847af..af60f5b02b 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -2567,7 +2567,38 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
QQmlListProperty<QObject> QQuickPropertyAnimation::targets()
{
Q_D(QQuickPropertyAnimation);
- return QQmlListProperty<QObject>(this, &(d->targets));
+ using ListPtr = QList<QPointer<QObject>> *;
+ using LP = QQmlListProperty<QObject>;
+ LP::AppendFunction appendFn = [](LP *prop, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->append(value);
+ };
+ LP::CountFunction countFn = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->size();
+ };
+
+ LP::AtFunction atFn = [](LP *prop, qsizetype index) -> QObject *
+ {
+ return static_cast<ListPtr>(prop->data)->at(index);
+ };
+
+ LP::ClearFunction clearFN = [](LP *prop)
+ {
+ return static_cast<ListPtr>(prop->data)->clear();
+ };
+
+ LP::ReplaceFunction replaceFn = [](LP *prop, qsizetype index, QObject *value)
+ {
+ static_cast<ListPtr>(prop->data)->replace(index, value);
+ };
+
+ LP::RemoveLastFunction removeLastFn = [](LP *prop)
+ {
+ static_cast<ListPtr>(prop->data)->removeLast();
+ };
+
+ return QQmlListProperty<QObject>(this, &(d->targets), appendFn, countFn, atFn, clearFN, replaceFn, removeLastFn);
}
/*!
@@ -2639,7 +2670,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
if (!d->propertyName.isEmpty())
props << d->propertyName;
- QList<QObject*> targets = d->targets;
+ QList<QPointer<QObject>> targets = d->targets;
if (d->target)
targets.append(d->target);
@@ -2668,10 +2699,14 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
for (int i = 0; i < props.count(); ++i) {
for (int j = 0; j < targets.count(); ++j) {
+ const auto& guarded = targets.at(j);
+ if (guarded.isNull())
+ continue;
+ QObject *target = guarded.get();
QQuickStateAction myAction;
QString errorMessage;
const QString &propertyName = props.at(i);
- myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage);
+ myAction.property = d->createProperty(target, propertyName, this, &errorMessage);
if (myAction.property.isValid()) {
if (usingDefaultProperties)
successfullyCreatedDefaultProperty = true;
diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h
index 56121221bf..97bf156d0d 100644
--- a/src/quick/util/qquickanimation_p_p.h
+++ b/src/quick/util/qquickanimation_p_p.h
@@ -279,7 +279,7 @@ public:
QObject *target;
QString propertyName;
QString properties;
- QList<QObject *> targets;
+ QList<QPointer<QObject>> targets;
QList<QObject *> exclude;
QString defaultProperties;
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index fdd9cb2f13..62e792199d 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -235,6 +235,7 @@ QQuickApplication::QQuickApplication(QObject *parent)
connect(guiApp, &QGuiApplication::applicationDisplayNameChanged,
this, &QQuickApplication::displayNameChanged);
+ connect(guiApp, &QGuiApplication::primaryScreenChanged, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenAdded, this, &QQuickApplication::updateScreens);
connect(guiApp, &QGuiApplication::screenRemoved, this, &QQuickApplication::updateScreens);
updateScreens();
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index b8167ebed3..a1f3fc64fd 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -1833,7 +1833,7 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- if (!relevant)
+ if (!item->clipRect().contains(itemPos))
return targets;
}
@@ -2116,11 +2116,6 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
if (item->acceptTouchEvents()) {
qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
- // If any parent filters the event, we're done.
- hasFiltered.clear();
- if (sendFilteredPointerEvent(&touchEvent, item))
- return;
-
// Deliver the touch event to the given item
qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
QCoreApplication::sendEvent(item, &touchEvent);
diff --git a/src/quick/util/qquicksystempalette.cpp b/src/quick/util/qquicksystempalette.cpp
index 7ccb19b5d3..01f72eab09 100644
--- a/src/quick/util/qquicksystempalette.cpp
+++ b/src/quick/util/qquicksystempalette.cpp
@@ -63,8 +63,8 @@ public:
The SystemPalette type provides access to the Qt application
palettes. This provides information about the standard colors used
for application windows, buttons and other features. These colors
- are grouped into three \e {color groups}: \c Active, \c Inactive,
- and \c Disabled. See the QPalette documentation for details about
+ are grouped into three \e {color groups}: \c active, \c inactive,
+ and \c disabled. See the QPalette documentation for details about
color groups and the properties provided by SystemPalette.
This can be used to color items in a way that provides a more
diff --git a/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
new file mode 100644
index 0000000000..82180fe465
--- /dev/null
+++ b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![entire]
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ visible: true
+
+ //![palette]
+ palette {
+ buttonText: "red"
+ button: "khaki"
+
+ disabled {
+ buttonText: "lavender"
+ button: "coral"
+ }
+ }
+ //![palette]
+
+ ColumnLayout {
+ id: layout
+ anchors.fill: parent
+ anchors.margins: 3
+ Button {
+ text: qsTr("Disabled button")
+ enabled: false
+ }
+
+ Button {
+ text: qsTr("Enabled button")
+ }
+
+ TextField {
+ Layout.fillWidth: true
+ placeholderText: "type something here"
+ }
+ }
+}
+//![entire]
diff --git a/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc
new file mode 100644
index 0000000000..7e12f53a6d
--- /dev/null
+++ b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc
@@ -0,0 +1,76 @@
+//! [detailed-description]
+A \1 provides a styled table header.
+It can either be used as an independent view or header for a \l TableView.
+
+You can add a header for a TableView by assigning a \1
+to the \l {TableView::syncView} property. The header and the table will
+then be kept in sync while flicking.
+
+By default, \1 displays
+\l {QAbstractItemModel::headerData()}{header data}
+from the \l {TableView::syncView}{sync view's} \l {TableView::model}{model}.
+If you don't wish to use this model, you can assign a different model to the
+\l {TableView::model}{model} property. If you assign a model that is a
+QAbstractItemModel, its header data will be used. Otherwise the data in
+the model will be used directly (for example, if you assign a model that
+is simply an array of strings).
+
+By default, \l textRole is set to \c "display", meaning that data from the
+model's \l {Qt::ItemDataRole}{Qt::DisplayRole} will be used. You can set
+this to another role name in order to have that data displayed instead.
+
+The application is responsible for placing the header at the
+correct location in the scene. You can add as many headers as you
+want to a single TableView, which can be useful if you for example want
+to place headers on all four sides of the table.
+
+The following snippet shows how you can add a horizontal and vertical header
+view to a table view:
+
+\snippet qtquickcontrols-headerview.qml 0
+
+A \1 will have
+\l {TableView::resizableColumns}{resizableColumns} set to \c true by default.
+//! [detailed-description]
+
+//! [syncView]
+This property holds the TableView to synchronize with.
+
+Once this property is bound to another TableView, both header and table
+will synchronize with regard to column widths, column spacing, and flicking
+\1.
+
+If the \l model is not explicitly set, then the header will use the syncView's
+model to label the columns.
+
+\sa model TableView
+//! [syncView]
+
+//! [model]
+This property holds the model providing data for the \1 header view.
+
+When model is not explicitly set, the header will use the syncView's
+model once syncView is set.
+
+If model is a QAbstractTableModel, its \1 headerData() will
+be accessed.
+
+If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
+will be accessed.
+
+Otherwise, the behavior is same as setting TableView::model.
+
+\sa TableView {TableView::model} {model} QAbstractTableModel
+//! [model]
+
+//! [textRole]
+This property holds the model role used to display text in each header cell.
+
+When the model has multiple roles, textRole can be set to determine which
+role should be displayed.
+
+If model is a QAbstractItemModel then it will default to "display"; otherwise
+it is empty.
+
+\sa QAbstractItemModel::roleNames()
+//! [textRole]
diff --git a/src/quickdialogs2/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
index f6cf1d8325..f05c4c83a9 100644
--- a/src/quickdialogs2/quickdialogs2/CMakeLists.txt
+++ b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
@@ -9,6 +9,7 @@ qt_internal_add_qml_module(QuickDialogs2
PLUGIN_TARGET qtquickdialogsplugin
DEPENDENCIES
QtQuick/auto
+ QtQuick.Dialogs.quickimpl/auto
SOURCES
qquickabstractdialog.cpp
qquickabstractdialog_p.h
diff --git a/src/quicktemplates2/qquickdeferredexecute.cpp b/src/quicktemplates2/qquickdeferredexecute.cpp
index 817415c492..635b7c142a 100644
--- a/src/quicktemplates2/qquickdeferredexecute.cpp
+++ b/src/quicktemplates2/qquickdeferredexecute.cpp
@@ -75,6 +75,15 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro
int propertyIndex = property.index();
int wasInProgress = enginePriv->inProgressCreations;
+ /* we don't want deferred properties to suddenly depend on arbitrary
+ other properties which might have trigerred the construction of
+ objects as a consequence of a read.
+ */
+ auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
+ auto cleanup = qScopeGuard([&](){
+ QtPrivate::restoreBindingStatus(bindingStatus);
+ });
+
for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
QQmlData::DeferredData *deferData = *dit;
@@ -139,6 +148,15 @@ void completeDeferred(QObject *object, const QString &property)
QQmlData *data = QQmlData::get(object);
QQmlComponentPrivate::DeferredState *state = deferredStates()->take(qHash(object, property));
if (data && state && !data->wasDeleted(object)) {
+ /* we don't want deferred properties to suddenly depend on arbitrary
+ other properties which might have trigerred the construction of
+ objects as a consequence of a read.
+ */
+ auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
+ auto cleanup = qScopeGuard([&](){
+ QtPrivate::restoreBindingStatus(bindingStatus);
+ });
+
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
QQmlComponentPrivate::completeDeferred(ep, state);
}
diff --git a/src/quicktemplates2/qquickheaderview.cpp b/src/quicktemplates2/qquickheaderview.cpp
index 94d5635ad1..298dbfbc05 100644
--- a/src/quicktemplates2/qquickheaderview.cpp
+++ b/src/quicktemplates2/qquickheaderview.cpp
@@ -47,16 +47,9 @@
\inherits TableView
\brief Provides a horizontal header view to accompany a \l TableView.
- A HorizontalHeaderView provides labeling of the columns of a \l TableView.
- To add a horizontal header to a TableView, bind the
- \l {HorizontalHeaderView::syncView} {syncView} property to the TableView:
+ \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
- \snippet qtquickcontrols2-headerview-simple.qml horizontal
-
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's horizontal headerData(); otherwise,
- the model's data().
+ \sa VerticalHeaderView
*/
/*!
@@ -66,112 +59,45 @@
\inherits TableView
\brief Provides a vertical header view to accompany a \l TableView.
- A VerticalHeaderView provides labeling of the rows of a \l TableView.
- To add a vertical header to a TableView, bind the
- \l {VerticalHeaderView::syncView} {syncView} property to the TableView:
-
- \snippet qtquickcontrols2-headerview-simple.qml vertical
+ \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's vertical headerData(); otherwise,
- the model's data().
+ \sa HorizontalHeaderView
*/
/*!
\qmlproperty TableView QtQuick::HorizontalHeaderView::syncView
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to column widths, column spacing, and flicking
- horizontally.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the columns.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {horizontally}
*/
/*!
\qmlproperty TableView QtQuick::VerticalHeaderView::syncView
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to row heights, row spacing, and flicking
- vertically.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the rows.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {vertically}
*/
/*!
\qmlproperty QVariant QtQuick::HorizontalHeaderView::model
- This property holds the model providing data for the horizontal header view.
-
- When model is not explicitly set, the header will use the syncView's
- model once syncView is set.
-
- If model is a QAbstractTableModel, its horizontal headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {horizontal}
*/
/*!
\qmlproperty QVariant QtQuick::VerticalHeaderView::model
- This property holds the model providing data for the vertical header view.
-
- When model is not explicitly set, it will be synchronized with syncView's model
- once syncView is set.
-
- If model is a QAbstractTableModel, its vertical headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {vertical}
*/
/*!
\qmlproperty QString QtQuick::HorizontalHeaderView::textRole
- This property holds the model role used to display text in each header cell.
-
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
-
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
/*!
\qmlproperty QString QtQuick::VerticalHeaderView::textRole
- This property holds the model role used to display text in each header cell.
-
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
-
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
-
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
QT_BEGIN_NAMESPACE
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp
index e045375f89..373e78f71c 100644
--- a/src/quicktemplates2/qquickmenu.cpp
+++ b/src/quicktemplates2/qquickmenu.cpp
@@ -745,6 +745,12 @@ QQuickMenu::~QQuickMenu()
// been destroyed before that is called.
while (d->contentModel->count() > 0)
d->removeItem(0, d->itemAt(0));
+
+ if (d->contentItem) {
+ const auto children = d->contentItem->childItems();
+ for (QQuickItem *child : std::as_const(children))
+ QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder);
+ }
}
/*!
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index 4ac642ccc4..de862cfc11 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -340,6 +340,7 @@ void QQuickPopupPrivate::closeOrReject()
dialog->reject();
else
q->close();
+ touchId = -1;
}
bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags)
diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp
index 3277ba524f..8a18d6266d 100644
--- a/src/quicktemplates2/qquickswipeview.cpp
+++ b/src/quicktemplates2/qquickswipeview.cpp
@@ -110,7 +110,7 @@ class QQuickSwipeViewPrivate : public QQuickContainerPrivate
Q_DECLARE_PUBLIC(QQuickSwipeView)
public:
- void resizeItem(QQuickItem *item);
+ void resizeItem(int index, QQuickItem *item);
void resizeItems();
static QQuickSwipeViewPrivate *get(QQuickSwipeView *view);
@@ -144,25 +144,29 @@ public:
int currentIndex = -1;
};
+void QQuickSwipeViewPrivate::resizeItem(int index, QQuickItem *item)
+{
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
+ if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
+ qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
+ item->setProperty("_q_QQuickSwipeView_warned", true);
+ }
+ if (orientation == Qt::Horizontal)
+ item->setPosition({index * (contentItem->width() + spacing), 0});
+ else
+ item->setPosition({0, index * (contentItem->height() + spacing)});
+ item->setSize(QSizeF(contentItem->width(), contentItem->height()));
+}
+
void QQuickSwipeViewPrivate::resizeItems()
{
Q_Q(QQuickSwipeView);
const int count = q->count();
for (int i = 0; i < count; ++i) {
QQuickItem *item = itemAt(i);
- if (item) {
- QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
- // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
- if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
- qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
- item->setProperty("_q_QQuickSwipeView_warned", true);
- }
- if (orientation == Qt::Horizontal)
- item->setPosition({i * (contentItem->width() + spacing), 0});
- else
- item->setPosition({0, i * (contentItem->height() + spacing)});
- item->setSize(QSizeF(contentItem->width(), contentItem->height()));
- }
+ if (item)
+ resizeItem(i, item);
}
}
@@ -301,6 +305,13 @@ QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object)
return new QQuickSwipeViewAttached(object);
}
+void QQuickSwipeView::componentComplete()
+{
+ Q_D(QQuickSwipeView);
+ QQuickContainer::componentComplete();
+ d->resizeItems();
+}
+
void QQuickSwipeView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickSwipeView);
@@ -312,7 +323,7 @@ void QQuickSwipeView::itemAdded(int index, QQuickItem *item)
{
Q_D(QQuickSwipeView);
if (isComponentComplete())
- item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height()));
+ d->resizeItem(index, item);
QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item));
if (attached)
QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index);
diff --git a/src/quicktemplates2/qquickswipeview_p.h b/src/quicktemplates2/qquickswipeview_p.h
index 8a09a37e22..7723a58374 100644
--- a/src/quicktemplates2/qquickswipeview_p.h
+++ b/src/quicktemplates2/qquickswipeview_p.h
@@ -93,6 +93,7 @@ Q_SIGNALS:
Q_REVISION(2, 2) void orientationChanged();
protected:
+ void componentComplete() override;
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void itemAdded(int index, QQuickItem *item) override;
void itemMoved(int index, QQuickItem *item) override;
diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp
index 9051eb15a9..cd6196e677 100644
--- a/src/quicktestutils/quick/viewtestutils.cpp
+++ b/src/quicktestutils/quick/viewtestutils.cpp
@@ -32,6 +32,7 @@
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
+#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtTest/QTest>
@@ -471,6 +472,10 @@ namespace QQuickTouchUtils {
}
+namespace QTest {
+ int Q_TESTLIB_EXPORT defaultMouseDelay();
+}
+
namespace QQuickTest {
/*! \internal
@@ -528,6 +533,88 @@ namespace QQuickTest {
return false;
return true;
}
+
+ // TODO maybe move the generic pointerPress/Move/Release functions to QTestLib later on
+
+ static Qt::MouseButton pressedTabletButton = Qt::NoButton;
+ static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier;
+
+ void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mousePress(window, button, modifiers, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).press(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ pressedTabletButton = button;
+ pressedTabletModifiers = modifiers;
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ button, 0.8, 0, 0, 0, 0, 0, modifiers);
+ break;
+ default:
+ qWarning() << "can't send a press event from" << dev;
+ break;
+ }
+ }
+
+ void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mouseMove(window, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).move(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers);
+ break;
+ default:
+ qWarning() << "can't send a move event from" << dev;
+ break;
+ }
+ }
+
+ void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
+ Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
+ {
+ switch (dev->type()) {
+ case QPointingDevice::DeviceType::Mouse:
+ case QPointingDevice::DeviceType::TouchPad:
+ QTest::mouseRelease(window, button, modifiers, p);
+ break;
+ case QPointingDevice::DeviceType::TouchScreen:
+ QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).release(pointId, p, window);
+ QQuickTouchUtils::flush(window);
+ break;
+ case QPointingDevice::DeviceType::Puck:
+ case QPointingDevice::DeviceType::Stylus:
+ case QPointingDevice::DeviceType::Airbrush:
+ QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
+ QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
+ Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers);
+ break;
+ default:
+ qWarning() << "can't send a press event from" << dev;
+ break;
+ }
+ }
+
}
QT_END_NAMESPACE
diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h
index fd38844446..feedaef76f 100644
--- a/src/quicktestutils/quick/viewtestutils_p.h
+++ b/src/quicktestutils/quick/viewtestutils_p.h
@@ -203,6 +203,17 @@ namespace QQuickTest {
[[nodiscard]] bool initView(QQuickView &v, const QUrl &url,
bool moveMouseOut = true, QByteArray *errorMessage = nullptr);
[[nodiscard]] bool showView(QQuickView &v, const QUrl &url);
+
+ void pointerPress(const QPointingDevice *dev, QQuickWindow *window,
+ int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier);
+
+ void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId,
+ const QPoint &p);
+
+ void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId,
+ const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier);
}
QT_END_NAMESPACE
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index d316d12588..c593ca2a81 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -1466,6 +1466,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
// top-level window always.
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ // It's not just the timestamp but also the globalPressPosition, velocity etc.
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1481,10 +1483,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
// See QTBUG-25831
QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ pressEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
e->setAccepted(pressEvent.isAccepted());
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
}
@@ -1544,6 +1548,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1557,6 +1562,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}