aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake.conf2
-rw-r--r--dependencies.yaml10
-rw-r--r--examples/qml/doc/src/qml-extending.qdoc2
-rw-r--r--examples/qml/networkaccessmanagerfactory/view.qml2
-rw-r--r--examples/qml/referenceexamples/coercion/person.h5
-rw-r--r--examples/quickcontrols2/gallery/gallery.qml2
-rw-r--r--examples/quickcontrols2/texteditor/example.md8
-rw-r--r--src/3rdparty/masm/stubs/WTFStubs.cpp6
-rw-r--r--src/qml/common/qjsnumbercoercion.h4
-rw-r--r--src/qml/common/qv4compileddata_p.h6
-rw-r--r--src/qml/debugger/qqmldebug.cpp13
-rw-r--r--src/qml/debugger/qqmldebugconnector.cpp2
-rw-r--r--src/qml/doc/images/qmlsc-compilation-scheme.pngbin0 -> 43468 bytes
-rw-r--r--src/qml/doc/src/cmake/qt_add_qml_module.qdoc11
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc6
-rw-r--r--src/qml/doc/src/external-resources.qdoc39
-rw-r--r--src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc48
-rw-r--r--src/qml/doc/src/qtqml-tool-qmlsc.qdoc53
-rw-r--r--src/qml/doc/src/qtqml.qdoc2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp22
-rw-r--r--src/qml/parser/qqmljs.g3
-rw-r--r--src/qml/qml/qqml.cpp2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp10
-rw-r--r--src/qml/qml/qqmlcontext.h2
-rw-r--r--src/qml/qml/qqmlengine.cpp2
-rw-r--r--src/qml/qml/qqmlengine_p.h4
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp3
-rw-r--r--src/qml/qml/qqmlfile.cpp148
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp27
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h5
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp11
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp11
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp5
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h1
-rw-r--r--src/qmldom/qqmldomtop.cpp8
-rw-r--r--src/qmlmodels/qqmlchangeset.cpp10
-rw-r--r--src/qmlmodels/qqmllistcompositor_p.h5
-rw-r--r--src/qmltest/quicktestresult.cpp3
-rw-r--r--src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc54
-rw-r--r--src/quick/items/qquickflickable.cpp41
-rw-r--r--src/quick/items/qquickgridview.cpp10
-rw-r--r--src/quick/items/qquickitem.cpp3
-rw-r--r--src/quick/items/qquickitemview.cpp19
-rw-r--r--src/quick/items/qquickitemview_p_p.h1
-rw-r--r--src/quick/items/qquicklistview.cpp10
-rw-r--r--src/quick/items/qquicktextedit.cpp44
-rw-r--r--src/quick/items/qquicktextedit_p_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp23
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp3
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp9
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h1
-rw-r--r--src/quickcontrols2/doc/src/qtquickcontrols2-delegates.qdoc19
-rw-r--r--src/quickcontrols2impl/qquickplatformtheme.cpp12
-rw-r--r--src/quickcontrols2impl/qquickplatformtheme_p.h2
-rw-r--r--src/quickcontrolstestutils/controlstestutils.cpp4
-rw-r--r--src/quickcontrolstestutils/controlstestutils_p.h6
-rw-r--r--src/quickcontrolstestutils/dialogstestutils_p.h2
-rw-r--r--src/quickdialogs2/quickdialogs2/CMakeLists.txt1
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp11
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp41
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h3
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt1
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp122
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h10
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h4
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp39
-rw-r--r--src/quicktemplates2/qquickdialogbuttonbox.cpp7
-rw-r--r--src/quicktemplates2/qquickitemdelegate.cpp1
-rw-r--r--src/quicktemplates2/qquicklabel_p_p.h2
-rw-r--r--src/quicktemplates2/qquickmenu.cpp3
-rw-r--r--src/quicktemplates2/qquickmonthgrid.cpp4
-rw-r--r--src/quicktemplates2/qquickpopup.cpp7
-rw-r--r--src/quicktemplates2/qquickpopup_p_p.h2
-rw-r--r--src/quicktemplates2/qquickscrollbar.cpp2
-rw-r--r--src/quicktemplates2/qquickselectionrectangle.cpp7
-rw-r--r--src/quicktemplates2/qquickspinbox.cpp3
-rw-r--r--src/quicktemplates2/qquicktextarea.cpp7
-rw-r--r--src/quicktestutils/quick/viewtestutils_p.h8
-rw-r--r--src/quicktestutils/quick/visualtestutils.cpp3
-rw-r--r--src/quicktestutils/quick/visualtestutils_p.h15
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml11
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml33
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/listIndices.qml8
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp41
-rw-r--r--tests/auto/qml/qmltyperegistrar/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt20
-rw-r--r--tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h17
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp10
-rw-r--r--tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h1
-rw-r--r--tests/auto/qml/qqmlapplicationengine/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt18
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml13
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml11
-rw-r--r--tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp82
-rw-r--r--tests/auto/qml/qqmlfile/tst_qqmlfile.cpp132
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp3
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp3
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp20
-rw-r--r--tests/auto/qmlls/qmlls/tst_qmlls.cpp3
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml20
-rw-r--r--tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp19
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml (renamed from tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml)0
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp40
-rw-r--r--tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml26
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp24
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST6
-rw-r--r--tests/auto/quick/qquicklistview/data/headerCrash.qml2
-rw-r--r--tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml2
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp86
-rw-r--r--tests/auto/quick/qquicklistview2/BLACKLIST3
-rw-r--r--tests/auto/quick/qquicklistview2/data/buttonDelegate.qml27
-rw-r--r--tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml30
-rw-r--r--tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp87
-rw-r--r--tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp80
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST2
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp28
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp4
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp1
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_container.qml84
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_control.qml82
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml82
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml2
-rw-r--r--tests/auto/quickcontrols2/cursor/tst_cursor.cpp14
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml55
-rw-r--r--tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp22
-rw-r--r--tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp15
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml11
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/data/window.qml11
-rw-r--r--tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp71
-rw-r--r--tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp2
-rw-r--r--tests/auto/quickcontrols2/snippets/CMakeLists.txt18
-rw-r--r--tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp2
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST3
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml71
-rw-r--r--tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp115
-rw-r--r--tests/manual/quickdialogs/dialogs/FileDialogPage.qml2
-rw-r--r--tools/CMakeLists.txt3
-rw-r--r--tools/qmlimportscanner/main.cpp375
-rw-r--r--tools/qmlls/CMakeLists.txt11
142 files changed, 2560 insertions, 487 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 40d4e4b152..8a8ac4692c 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,2 @@
-set(QT_REPO_MODULE_VERSION "6.3.1")
+set(QT_REPO_MODULE_VERSION "6.3.2")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "")
diff --git a/dependencies.yaml b/dependencies.yaml
index b5167fa1be..32124940cb 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,16 +1,16 @@
dependencies:
../qtbase:
- ref: 7ffefd9c2b017fcaa8e5382f84ddd4bafbe733fa
+ ref: f9e8577cf4478cab4f0e60dacb5735a6c59a3dd4
required: true
../qtimageformats:
- ref: f9f81a7ce3a84105e2a4b1bf85778dd5cbb1bf0b
+ ref: a0700f4f63095f567787c991deb4f82dfe935c2c
required: false
../qtlanguageserver:
- ref: f03e788c07f7e861886b669f026f32a235f25a58
+ ref: 023bc6c3f1d09d013d93a7eaf7731273ba308083
required: false
../qtshadertools:
- ref: 7e4805b1d815ad5bfa1530cfffefe71b958dff7e
+ ref: 8847b19ddc1997dc56c0b3098c3b5502886864f0
required: false
../qtsvg:
- ref: 5ff1a0e32bfee98f4bcacfb1c6f04383ceb3c304
+ ref: 43a75930778b9a8f1faf68f311a987d2294e7e3b
required: false
diff --git a/examples/qml/doc/src/qml-extending.qdoc b/examples/qml/doc/src/qml-extending.qdoc
index 310436759e..ca244d7fce 100644
--- a/examples/qml/doc/src/qml-extending.qdoc
+++ b/examples/qml/doc/src/qml-extending.qdoc
@@ -165,7 +165,7 @@ developed in the previous examples into two types - a \c Boy and a \c Girl.
\section1 Declare Boy and Girl
-\snippet referenceexamples/coercion/person.h 0
+\snippet referenceexamples/coercion/person.h 1
The Person class remains unaltered in this example and the Boy and Girl C++
classes are trivial extensions of it. The types and their QML name are
diff --git a/examples/qml/networkaccessmanagerfactory/view.qml b/examples/qml/networkaccessmanagerfactory/view.qml
index da90e531b5..1c496bd623 100644
--- a/examples/qml/networkaccessmanagerfactory/view.qml
+++ b/examples/qml/networkaccessmanagerfactory/view.qml
@@ -53,5 +53,5 @@ import QtQuick 2.0
Image {
width: 200
height: 100
- source: "http://doc.qt.io/qt-5/images/logo.png"
+ source: "http://doc.qt.io/qt-6/images/logo.png"
}
diff --git a/examples/qml/referenceexamples/coercion/person.h b/examples/qml/referenceexamples/coercion/person.h
index 1ec095e841..aa2100f2da 100644
--- a/examples/qml/referenceexamples/coercion/person.h
+++ b/examples/qml/referenceexamples/coercion/person.h
@@ -77,8 +77,7 @@ private:
int m_shoeSize = 0;
};
-
-// ![0]
+// ![1]
class Boy : public Person
{
Q_OBJECT
@@ -97,6 +96,6 @@ public:
};
//! [girl class]
-// ![0]
+// ![1]
#endif // PERSON_H
diff --git a/examples/quickcontrols2/gallery/gallery.qml b/examples/quickcontrols2/gallery/gallery.qml
index 58d1b49ece..1888a6b4c6 100644
--- a/examples/quickcontrols2/gallery/gallery.qml
+++ b/examples/quickcontrols2/gallery/gallery.qml
@@ -66,7 +66,7 @@ ApplicationWindow {
let displayingControl = listView.currentIndex !== -1
let currentControlName = displayingControl
? listView.model.get(listView.currentIndex).title.toLowerCase() : ""
- let url = "https://doc.qt.io/qt-5/"
+ let url = "https://doc.qt.io/qt-6/"
+ (displayingControl
? "qml-qtquick-controls2-" + currentControlName + ".html"
: "qtquick-controls2-qmlmodule.html");
diff --git a/examples/quickcontrols2/texteditor/example.md b/examples/quickcontrols2/texteditor/example.md
index 0ee17cdb9b..e385227e3b 100644
--- a/examples/quickcontrols2/texteditor/example.md
+++ b/examples/quickcontrols2/texteditor/example.md
@@ -71,9 +71,9 @@ John MacFarlane writes:
## Hyperlinks
Hyperlinks can be written with the link text first, and the URL immediately
-following: [Qt Assistant](http://doc.qt.io/qt-5/qtassistant-index.html)
+following: [Qt Assistant](http://doc.qt.io/qt-6/qtassistant-index.html)
-A plain url is automatically recognized: https://doc.qt.io/qt-5/qml-qtquick-text.html
+A plain url is automatically recognized: https://doc.qt.io/qt-6/qml-qtquick-text.html
There are also "reference links" where the link text is first labeled
and then the URL for the label is given elsewhere:
@@ -104,7 +104,7 @@ to form a task list.
- List items can include images: ![red square](red.png)
- and even nested quotes, like this:
- The [Qt Documentation](https://doc.qt.io/qt-5/qml-qtquick-textedit.html#details)
+ The [Qt Documentation](https://doc.qt.io/qt-6/qml-qtquick-textedit.html#details)
points out that
> The TextEdit item displays a block of editable, formatted text.
>
@@ -155,7 +155,7 @@ One of the GitHub extensions is support for tables:
# Related Work
Some Qt Widgets also support Markdown.
-[QTextEdit](https://doc.qt.io/qt-5/qtextedit.html) has similar WYSIWYG
+[QTextEdit](https://doc.qt.io/qt-6/qtextedit.html) has similar WYSIWYG
editing features as TextEdit and TextArea: you can edit the rendered text
directly. You can use
[QTextDocument::toMarkdown](https://doc-snapshots.qt.io/qt5-dev/qtextdocument.html#toMarkdown)
diff --git a/src/3rdparty/masm/stubs/WTFStubs.cpp b/src/3rdparty/masm/stubs/WTFStubs.cpp
index f408b355f5..facba7d937 100644
--- a/src/3rdparty/masm/stubs/WTFStubs.cpp
+++ b/src/3rdparty/masm/stubs/WTFStubs.cpp
@@ -44,6 +44,10 @@
#include <qbytearray.h> // qvsnprintf
#include <FilePrintStream.h>
+#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X)
+#include <MacroAssemblerX86Common.h>
+#endif
+
namespace WTF {
void* fastMalloc(size_t size)
@@ -142,8 +146,6 @@ void WTFInvokeCrashHook()
#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X)
-#include <MacroAssemblerX86Common.h>
-
JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2;
#endif
diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h
index 2517442bb6..0e233747ff 100644
--- a/src/qml/common/qjsnumbercoercion.h
+++ b/src/qml/common/qjsnumbercoercion.h
@@ -48,6 +48,10 @@ QT_BEGIN_NAMESPACE
class QJSNumberCoercion
{
public:
+ static constexpr bool isInteger(double d) {
+ return equals(d, d) && equals(static_cast<int>(d), d);
+ }
+
static constexpr int toInteger(double d) {
if (!equals(d, d))
return 0;
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index b14e523ab2..35119364d9 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -129,7 +129,7 @@ struct TableIterator
struct Location
{
Location() : m_data(QSpecialIntegerBitfieldZero) {}
- Location(quint32 l, quint32 c)
+ Location(quint32 l, quint32 c) : Location()
{
m_data.set<LineField>(l);
m_data.set<ColumnField>(c);
@@ -182,7 +182,7 @@ struct RegExp
};
RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
- RegExp(quint32 flags, quint32 stringIndex)
+ RegExp(quint32 flags, quint32 stringIndex) : RegExp()
{
m_data.set<FlagsField>(flags);
m_data.set<StringIndexField>(stringIndex);
@@ -211,7 +211,7 @@ struct Lookup
quint32 nameIndex() const { return m_data.get<NameIndexField>(); }
Lookup() : m_data(QSpecialIntegerBitfieldZero) {}
- Lookup(Type type, quint32 nameIndex)
+ Lookup(Type type, quint32 nameIndex) : Lookup()
{
m_data.set<TypeAndFlagsField>(type);
m_data.set<NameIndexField>(nameIndex);
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 58b8ea2c4f..0ca56f3a70 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -44,17 +44,26 @@
#include <private/qqmlengine_p.h>
#include <private/qv4compileddata_p.h>
+#include <atomic>
#include <cstdio>
QT_REQUIRE_CONFIG(qml_debug);
QT_BEGIN_NAMESPACE
+#if __cplusplus >= 202002L
+# define Q_ATOMIC_FLAG_INIT {}
+#else
+# define Q_ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT // deprecated in C++20
+#endif
+
+static std::atomic_flag s_printedWarning = Q_ATOMIC_FLAG_INIT;
+
QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning)
{
- if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning)
+ if (printWarning && !s_printedWarning.test_and_set(std::memory_order_relaxed))
fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n");
- QQmlEnginePrivate::qml_debugging_enabled = true;
+ QQmlEnginePrivate::qml_debugging_enabled.store(true, std::memory_order_relaxed);
}
/*!
diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp
index dabe6bfabe..0077e655bb 100644
--- a/src/qml/debugger/qqmldebugconnector.cpp
+++ b/src/qml/debugger/qqmldebugconnector.cpp
@@ -111,7 +111,7 @@ QQmlDebugConnector *QQmlDebugConnector::instance()
if (!params)
return nullptr;
- if (!QQmlEnginePrivate::qml_debugging_enabled) {
+ if (!QQmlEnginePrivate::qml_debugging_enabled.load(std::memory_order_relaxed)) {
if (!params->arguments.isEmpty()) {
qWarning().noquote() << QString::fromLatin1(
"QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging "
diff --git a/src/qml/doc/images/qmlsc-compilation-scheme.png b/src/qml/doc/images/qmlsc-compilation-scheme.png
new file mode 100644
index 0000000000..8a7785ea4b
--- /dev/null
+++ b/src/qml/doc/images/qmlsc-compilation-scheme.png
Binary files differ
diff --git a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
index 05ca519437..ae8306af6d 100644
--- a/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
+++ b/src/qml/doc/src/cmake/qt_add_qml_module.qdoc
@@ -283,6 +283,17 @@ target will be the \c target followed by \c{_qmllint}. An \c{all_qmllint}
target which depends on all the individual \c{*_qmllint} targets is also
provided as a convenience.
+\target qml-naming-js-files
+\section2 Naming conventions for \c{.js} files
+
+JavaScript file names that are intended to be addressed as components should
+start with an uppercase letter.
+
+Alternatively, you may use lowercase file names and set the source file
+property
+\l{cmake-source-file-property-QT_QML_SOURCE_TYPENAME}{QT_QML_SOURCE_TYPE_NAME}
+to the desired type name.
+
\target qml-cmake-singletons
\section2 Singletons
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index 86a9f1d1f7..a73eb92416 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -274,9 +274,9 @@ Notice the parameter and return type specified after the colon. You can use \l
{QML Basic Types}{basic types} and \l {QML Object Types}{object types} as type
names.
-If the type is omitted in QML, then you must specify QVariant as type with
-Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod.
-
+If the type is omitted or specified as \c var in QML, then you must pass
+QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
+QMetaObject::invokeMethod.
\section2 Connecting to QML Signals
diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc
index 0999abc5af..cd8c895e17 100644
--- a/src/qml/doc/src/external-resources.qdoc
+++ b/src/qml/doc/src/external-resources.qdoc
@@ -26,52 +26,39 @@
****************************************************************************/
/*!
- \externalpage http://www.ecma-international.org/publications/standards/Ecma-262.htm
+ \externalpage https://www.ecma-international.org/publications-and-standards/standards/ecma-262/
\title ECMA-262
*/
/*!
- \externalpage http://qmlbook.github.io/
- \title Qt5 Cadaques
-*/
-
-/*!
- \externalpage http://www.w3schools.com/jsref/default.asp
+ \externalpage https://www.w3schools.com/jsref/default.asp
\title W3Schools JavaScript Reference
*/
/*!
\externalpage https://tc39.es/ecma262/#sec-date-objects
- \title {ECMAScript Specification of Date}
+ \title ECMAScript Specification of Date
*/
/*!
- \externalpage https://www.froglogic.com/squish/gui-testing
- \title Squish
-*/
-/*!
- \externalpage http://doc.qt.io/GammaRay
- \title GammaRay
-*/
-/*!
- \externalpage http://doc.qt.io/QtQmlLive
- \title QmlLive
+ \externalpage https://www.qt.io/product/testing-tools#squish
+ \title Squish GUI Test Automation Tool
*/
/*!
- \externalpage http://doc.qt.io/qtcreator/creator-debugging-qml.html
- \title QML Debugger
+ \externalpage https://doc.qt.io/GammaRay
+ \title GammaRay Manual
*/
/*!
- \externalpage http://doc.qt.io/qtcreator/creator-qml-performance-monitor.html
- \title QML Profiler
+ \externalpage https://doc.qt.io/QtQmlLive
+ \title QmlLive Manual
*/
/*!
- \externalpage http://doc.qt.io/qtcreator/index.html
- \title Qt Creator Manual
+ \externalpage https://doc.qt.io/qtcreator/creator-debugging-qml.html
+ \title Qt Creator: QML Debugger
*/
/*!
- \externalpage https://doc.qt.io/qtcreator/creator-project-creating.html#creating-resource-files
- \title Creating Resource Files
+ \externalpage https://doc.qt.io/qtcreator/creator-qml-performance-monitor.html
+ \title Qt Creator: QML Profiler
*/
/*!
\externalpage https://fontawesome.com/
diff --git a/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc
new file mode 100644
index 0000000000..cb25e8226e
--- /dev/null
+++ b/src/qml/doc/src/qtqml-qtquick-compiler-tech.qdoc
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+/*!
+\page qtqml-qtquick-compiler-tech.html
+\title Qt Quick Compiler
+\brief Overview of Qt Quick Compiler components
+
+The Qt Quick Compiler consist of two components:
+\list
+ \li \l {QML Type Compiler}
+ \li \l {QML Script Compiler}
+\endlist
+
+The QML Type Compiler (\e qmltc) compiles QML object structures into C++ classes.
+The QML Script Compiler compiles functions and expressions in QML files of an
+application into C++ code.
+
+\e qmltc uses an all-or-nothing approach, and compilation simply fails if
+some unsupported language feature is encountered.
+
+In the case of \e qmlsc JavaScript sets limitations on compiling with \e qmlsc.
+For more information see \l {Limitations when compiling JavaScript}.
+*/
diff --git a/src/qml/doc/src/qtqml-tool-qmlsc.qdoc b/src/qml/doc/src/qtqml-tool-qmlsc.qdoc
new file mode 100644
index 0000000000..5546e858f8
--- /dev/null
+++ b/src/qml/doc/src/qtqml-tool-qmlsc.qdoc
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+/*!
+\page qtqml-tool-qmlsc.html
+\title QML Script Compiler
+\brief A tool to compile functions and expressions in QML.
+
+The QML Script Compiler will compile functions and expressions in QML files of an
+application into C++ code within limitations set by the nature of JavaScript.
+It replaces \e qmlcachegen, and simply generates C++ code in addition to byte
+code for functions that can be exhaustively analyzed. The following flow chart
+explains the compilation of \e qmlsc.
+
+\image qmlsc-compilation-scheme.png
+
+\section1 Limitations when compiling JavaScript
+
+Many JavaScript constructs cannot be efficiently represented in C++. \e qmlsc
+skips the C++ code generation for functions that contain such constructs and
+only generates byte code to be interpreted. Although, most common QML expressions
+are rather simple: value lookups on QObjects, arithmetics, simple if/else or loop
+constructs. Those can easily be expressed in C++, and doing so makes your
+application run faster.
+
+\e qmlsc is available for commercial customers and some of its features are
+merged into \e qmlcachegen, which continues to be available with all versions of
+Qt.
+*/
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index b8385c0ea7..a6f14fdc71 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -158,6 +158,8 @@ the QML code to interact with C++ code.
Further information for writing QML applications:
\list
\li \l {The QML Reference}
+ \li \l {Qt Quick Compiler}
+ - overview of Qt Quick Compiler components
\li \l {QML Applications}
- essential information for application development with QML and Qt
Quick
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 63e02ce631..8ea0c94e7a 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -442,8 +442,8 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(function->aotFunction);
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
- function->compiledFunction->location.line,
- function->compiledFunction->location.column);
+ function->compiledFunction->location.line(),
+ function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
const qsizetype numFunctionArguments = function->aotFunction->argumentTypes.size();
@@ -461,9 +461,17 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ASSERT(argumentType.sizeOf() > 0);
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
- argumentType.construct(arg);
- if (frame->argc() > i)
- QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+
+ if (argumentType == QMetaType::fromType<QVariant>()) {
+ if (frame->argc() > i)
+ new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]);
+ else
+ new (arg) QVariant();
+ } else {
+ argumentType.construct(arg);
+ if (frame->argc() > i)
+ QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg);
+ }
transformedArguments[i] = arg;
}
@@ -525,8 +533,8 @@ ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)
Function *function = frame->v4Function;
Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(),
function->executableCompilationUnit()->fileName(),
- function->compiledFunction->location.line,
- function->compiledFunction->location.column);
+ function->compiledFunction->location.line(),
+ function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index e20e861f8b..d4629f2bdd 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1663,6 +1663,9 @@ Type: UiQualifiedId;
} break;
./
+Type: T_VAR;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
Type: T_VOID;
/.
case $rule_number: {
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 8781e4bf50..54d1be7d59 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -506,7 +506,7 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
const QTypeRevision added = revisionClassInfo(
type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromMinorVersion(0));
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
const QTypeRevision removed = revisionClassInfo(
type.classInfoMetaObject, "QML.RemovedInVersion");
const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index addee70b6b..f54eee3e72 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -862,8 +862,16 @@ QObject *QQmlComponent::create(QQmlContext *context)
Q_D(QQmlComponent);
QObject *rv = d->doBeginCreate(this, context);
- if (rv)
+ if (rv) {
completeCreate();
+ } else if (d->state.completePending) {
+ // overridden completCreate might assume that
+ // the object has actually been created
+ ++creationDepth.localData();
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->engine);
+ d->complete(ep, &d->state);
+ --creationDepth.localData();
+ }
if (rv && !d->requiredProperties().empty()) {
delete rv;
return nullptr;
diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h
index 82d15b8102..2da9541e5a 100644
--- a/src/qml/qml/qqmlcontext.h
+++ b/src/qml/qml/qqmlcontext.h
@@ -108,6 +108,4 @@ private:
};
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QList<QObject*>)
-
#endif // QQMLCONTEXT_H
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index e3840c8011..9963e4d957 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -163,7 +163,7 @@ QT_BEGIN_NAMESPACE
\endcode
*/
-bool QQmlEnginePrivate::qml_debugging_enabled = false;
+std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
bool QQmlEnginePrivate::s_designerMode = false;
bool QQmlEnginePrivate::designerMode()
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 630d32736b..97c2f95450 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -80,6 +80,8 @@
#include <QtCore/qstring.h>
#include <QtCore/qthread.h>
+#include <atomic>
+
QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
@@ -250,7 +252,7 @@ public:
static bool designerMode();
static void activateDesignerMode();
- static bool qml_debugging_enabled;
+ static std::atomic<bool> qml_debugging_enabled;
mutable QMutex networkAccessManagerMutex;
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index ecac16102d..e978ca7d9e 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -184,7 +184,8 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char
/*!
\macro Q_IMPORT_QML_PLUGIN(PluginName)
- \relates <QQmlExtensionPlugin>
+ \since 6.2
+ \relates QQmlExtensionPlugin
Ensures the plugin whose metadata-declaring class is named \a PluginName
is linked into static builds.
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index 4db8981975..dc2734d7d1 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -516,58 +516,91 @@ bool QQmlFile::isLocalFile(const QUrl &url)
{
QString scheme = url.scheme();
- if ((scheme.length() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) ||
- (scheme.length() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) {
+ // file: URLs with two slashes following the scheme can be interpreted as local files
+ // where the slashes are part of the path. Therefore, disregard the authority.
+ // See QUrl::toLocalFile().
+ if (scheme.length() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive))
return true;
+ if (scheme.length() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive))
+ return url.authority().isEmpty();
+
#if defined(Q_OS_ANDROID)
- } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) {
- return true;
+ if ((scheme.length() == 6
+ && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
+ || (scheme.length() == 7
+ && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))) {
+ return url.authority().isEmpty();
+ }
#endif
- } else {
+ return false;
+}
+
+static bool hasSchemeAndNoAuthority(const QString &url, const char *scheme, qsizetype schemeLength)
+{
+ const qsizetype urlLength = url.length();
+
+ if (urlLength < schemeLength + 1)
+ return false;
+
+ if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive))
+ return false;
+
+ if (url[schemeLength] != QLatin1Char(':'))
return false;
+
+ if (urlLength < schemeLength + 3)
+ return true;
+
+ const QLatin1Char slash('/');
+ if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
+ // Exactly two slashes denote an authority. We don't want that.
+ if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
+ return false;
}
+
+ return true;
}
/*!
Returns true if \a url is a local file that can be opened with QFile.
-Local file urls have either a qrc:/ or file:// scheme.
+Local file urls have either a qrc: or file: scheme.
-\note On Android, urls with assets:/ scheme are also considered local files.
+\note On Android, urls with assets: or content: scheme are also considered local files.
*/
bool QQmlFile::isLocalFile(const QString &url)
{
- if (url.length() < 5 /* qrc:/ */)
+ if (url.length() < 4 /* qrc: */)
return false;
- QChar f = url[0];
-
- if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
-
- return url.length() >= 7 /* file:// */ &&
- url.startsWith(QLatin1String(file_string), Qt::CaseInsensitive) &&
- url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
-
- } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
-
- return url.length() >= 5 /* qrc:/ */ &&
- url.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive) &&
- url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
-
+ switch (url[0].toLatin1()) {
+ case 'f':
+ case 'F': {
+ // file: URLs with two slashes following the scheme can be interpreted as local files
+ // where the slashes are part of the path. Therefore, disregard the authority.
+ // See QUrl::toLocalFile().
+ const qsizetype fileLength = strlen(file_string);
+ return url.startsWith(QLatin1String(file_string, file_string + fileLength),
+ Qt::CaseInsensitive)
+ && url.length() > fileLength
+ && url[fileLength] == QLatin1Char(':');
}
+ case 'q':
+ case 'Q':
+ return hasSchemeAndNoAuthority(url, qrc_string, strlen(qrc_string));
#if defined(Q_OS_ANDROID)
- else if (f == QLatin1Char('a') || f == QLatin1Char('A')) {
- return url.length() >= 8 /* assets:/ */ &&
- url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) &&
- url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/');
- } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) {
- return url.length() >= 9 /* content:/ */ &&
- url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) &&
- url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/');
- }
+ case 'a':
+ case 'A':
+ return hasSchemeAndNoAuthority(url, assets_string, strlen(assets_string));
+ case 'c':
+ case 'C':
+ return hasSchemeAndNoAuthority(url, content_string, strlen(content_string));
#endif
+ default:
+ break;
+ }
return false;
}
@@ -585,13 +618,10 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
}
#if defined(Q_OS_ANDROID)
- else if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0) {
- if (url.authority().isEmpty())
- return url.toString();
- return QString();
- } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) {
- return url.toString();
- }
+ if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0)
+ return url.authority().isEmpty() ? url.toString() : QString();
+ if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0)
+ return url.authority().isEmpty() ? url.toString() : QString();
#endif
return url.toLocalFile();
@@ -603,11 +633,28 @@ static QString toLocalFile(const QString &url)
if (!file.isLocalFile())
return QString();
- //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
+ // QUrl::toLocalFile() interprets two slashes as part of the path.
+ // Therefore windows hostnames like "//servername/path/to/file.txt" are preserved.
return file.toLocalFile();
}
+static bool isDoubleSlashed(const QString &url, qsizetype offset)
+{
+ const qsizetype urlLength = url.length();
+ if (urlLength < offset + 2)
+ return false;
+
+ const QLatin1Char slash('/');
+ if (url[offset] != slash || url[offset + 1] != slash)
+ return false;
+
+ if (urlLength < offset + 3)
+ return true;
+
+ return url[offset + 2] != slash;
+}
+
/*!
If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
empty string.
@@ -615,23 +662,28 @@ empty string.
QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
{
if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) {
- if (url.length() > 6)
- return QLatin1Char(':') + QStringView{url}.mid(6);
- return QString();
+ // Exactly two slashes are bad because that's a URL authority.
+ // One slash is fine and >= 3 slashes are file.
+ if (url.length() == 6 || url[6] != QLatin1Char('/')) {
+ Q_ASSERT(isDoubleSlashed(url, strlen("qrc:")));
+ return QString();
+ }
+ Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
+ return QLatin1Char(':') + QStringView{url}.mid(6);
}
if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
+ Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
if (url.length() > 4)
return QLatin1Char(':') + QStringView{url}.mid(4);
- return QString();
+ return QStringLiteral(":");
}
#if defined(Q_OS_ANDROID)
- else if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) {
- return url;
- } else if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive)) {
- return url;
- }
+ if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive))
+ return isDoubleSlashed(url, strlen("assets:")) ? QString() : url;
+ if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive))
+ return isDoubleSlashed(url, strlen("content:")) ? QString() : url;
#endif
return toLocalFile(url);
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index cd325ae971..e5f2da7a22 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -586,8 +586,11 @@ void QQmlJSCodeGenerator::generate_LoadReg(int reg)
void QQmlJSCodeGenerator::generate_StoreReg(int reg)
{
INJECT_TRACE_INFO(generate_StoreReg);
-
Q_ASSERT(m_state.accumulatorIn.isValid());
+
+ if (isArgument(reg))
+ reject(u"writing into a function argument"_qs);
+
const QString var = registerVariable(reg);
m_body.setWriteRegister(var);
if (var.isEmpty())
@@ -812,6 +815,15 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QString baseName = use(registerVariable(base));
const QString indexName = use(m_state.accumulatorVariableIn);
+ const QString voidAssignment = u" "_qs + m_state.accumulatorVariableOut + u" = "_qs +
+ conversion(m_typeResolver->globalType(m_typeResolver->voidType()),
+ m_state.accumulatorOut, QString()) + u";\n"_qs;
+
+ if (!m_typeResolver->isIntegral(m_state.accumulatorIn)) {
+ m_body += u"if (!QJSNumberCoercion::isInteger("_qs + indexName + u"))\n"_qs
+ + voidAssignment
+ + u"else "_qs;
+ }
// Our QQmlListProperty only keeps plain QObject*.
const auto valueType = m_typeResolver->valueType(baseType);
const auto elementType = m_typeResolver->globalType(
@@ -824,9 +836,8 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
conversion(elementType, m_state.accumulatorOut,
baseName + u".at(&"_qs + baseName + u", "_qs
+ indexName + u')') + u";\n"_qs;
- m_body += u"else\n"_qs;
- m_body += u" "_qs + m_state.accumulatorVariableOut + u" = {}"_qs;
- m_body += u";\n"_qs;
+ m_body += u"else\n"_qs
+ + voidAssignment;
}
void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
@@ -837,6 +848,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
m_body.setWriteRegister(QString());
const QQmlJSRegisterContent baseType = registerType(base);
+ const QQmlJSRegisterContent indexType = registerType(index);
if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) {
reject(u"StoreElement with non-list base type or non-numeric arguments"_qs);
@@ -855,8 +867,11 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType(
m_typeResolver->containedType(valueType)));
- m_body += u"if ("_qs + indexName + u" >= 0 && "_qs + indexName
- + u" < "_qs + baseName + u".count(&"_qs + baseName
+ m_body += u"if ("_qs;
+ if (!m_typeResolver->isIntegral(indexType))
+ m_body += u"QJSNumberCoercion::isInteger("_qs + indexName + u") && "_qs;
+ m_body += indexName + u" >= 0 && "_qs
+ + indexName + u" < "_qs + baseName + u".count(&"_qs + baseName
+ u"))\n"_qs;
m_body += u" "_qs + baseName + u".replace(&"_qs + baseName
+ u", "_qs + indexName + u", "_qs;
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index a363629924..1048623bff 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -347,6 +347,11 @@ private:
return m_typeResolver->jsGlobalObject()->property(u"Math"_qs).type();
}
+ bool isArgument(int registerIndex) const
+ {
+ return registerIndex >= QV4::CallData::OffsetCount && registerIndex < firstRegisterIndex();
+ }
+
int firstRegisterIndex() const
{
return QV4::CallData::OffsetCount + m_function->argumentTypes.count();
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index fb4288b56d..8912f5c70b 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -1124,6 +1124,12 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
m_inlineComponentName.toString(), { m_currentScope, revision });
m_nextIsInlineComponent = false;
}
+
+ addDefaultProperties();
+ Q_ASSERT(m_currentScope->scopeType() == QQmlJSScope::QMLScope);
+ m_qmlTypes.append(m_currentScope);
+
+ m_objectDefinitionScopes << m_currentScope;
} else {
enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType,
definition->firstSourceLocation());
@@ -1133,11 +1139,6 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
- addDefaultProperties();
- if (m_currentScope->scopeType() == QQmlJSScope::QMLScope)
- m_qmlTypes.append(m_currentScope);
-
- m_objectDefinitionScopes << m_currentScope;
return true;
}
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index d7bdc714a6..01bf600fd9 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -26,6 +26,17 @@
**
****************************************************************************/
+#include <qglobal.h>
+
+// GCC 11 thinks diagMsg.fixSuggestion.fixes.d.ptr is somehow uninitialized in
+// QList::emplaceBack(), probably called from QQmlJsLogger::log()
+// Ditto for GCC 12, but it emits a different warning
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wuninitialized")
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
+#include <qlist.h>
+QT_WARNING_POP
+
#include "qqmljslogger_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index d1e1fa66f8..e38038d79a 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -255,6 +255,11 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
return isNumeric(containedType(type));
}
+bool QQmlJSTypeResolver::isIntegral(const QQmlJSRegisterContent &type) const
+{
+ return containedType(type) == m_intType;
+}
+
bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
{
return type == m_intType || type == m_realType || type == m_floatType || type == m_boolType
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 491391f801..85daa35cb1 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -107,6 +107,7 @@ public:
bool isPrimitive(const QQmlJSRegisterContent &type) const;
bool isNumeric(const QQmlJSRegisterContent &type) const;
+ bool isIntegral(const QQmlJSRegisterContent &type) const;
bool canConvertFromTo(const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const;
bool canConvertFromTo(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const;
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index ed62310075..01ea9ca2bd 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -49,7 +49,6 @@
#include <QtQml/private/qqmljsastvisitor_p.h>
#include <QtQml/private/qqmljsast_p.h>
-#include <QtCore/QAtomicInt>
#include <QtCore/QBasicMutex>
#include <QtCore/QCborArray>
#include <QtCore/QDebug>
@@ -158,11 +157,14 @@ DomUniverse::DomUniverse(QString universeName, Options options):
std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomUniverse> univ)
{
- static QAtomicInt counter(0);
+ const auto next = [] {
+ static std::atomic<int> counter(0);
+ return counter.fetch_add(1, std::memory_order_relaxed) + 1;
+ };
if (univ)
return univ;
return std::shared_ptr<DomUniverse>(
- new DomUniverse(QLatin1String("universe") + QString::number(++counter)));
+ new DomUniverse(QLatin1String("universe") + QString::number(next())));
}
DomItem DomUniverse::create(QString universeName, Options options)
diff --git a/src/qmlmodels/qqmlchangeset.cpp b/src/qmlmodels/qqmlchangeset.cpp
index ba876b42e2..99def9567c 100644
--- a/src/qmlmodels/qqmlchangeset.cpp
+++ b/src/qmlmodels/qqmlchangeset.cpp
@@ -557,16 +557,17 @@ void QQmlChangeSet::change(QVector<Change> *changes)
QDebug operator <<(QDebug debug, const QQmlChangeSet &set)
{
+ QDebugStateSaver stateSaver(debug);
debug.nospace() << "QQmlChangeSet(";
const QVector<QQmlChangeSet::Change> &removes = set.removes();
for (const QQmlChangeSet::Change &remove : removes)
- debug << remove;
+ debug << remove << ' ';
const QVector<QQmlChangeSet::Change> &inserts = set.inserts();
for (const QQmlChangeSet::Change &insert : inserts)
- debug << insert;
+ debug << insert << ' ';
const QVector<QQmlChangeSet::Change> &changes = set.changes();
for (const QQmlChangeSet::Change &change : changes)
- debug << change;
+ debug << change << ' ';
return debug.nospace() << ')';
}
@@ -576,7 +577,8 @@ QDebug operator <<(QDebug debug, const QQmlChangeSet &set)
QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change)
{
- return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space();
+ QDebugStateSaver stateSaver(debug);
+ return debug.nospace() << "Change(" << change.index << ',' << change.count << ')';
}
QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmllistcompositor_p.h b/src/qmlmodels/qqmllistcompositor_p.h
index 7bf20397fd..797a457e8c 100644
--- a/src/qmlmodels/qqmllistcompositor_p.h
+++ b/src/qmlmodels/qqmllistcompositor_p.h
@@ -308,6 +308,10 @@ Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE);
inline QQmlListCompositor::iterator::iterator() {}
+QT_WARNING_PUSH
+// GCC isn't wrong, as groupCount is public in iterator, but we tried Q_ASSUME(),
+// right in front of the loops, and it didn't help, so we disable the warning:
+QT_WARNING_DISABLE_GCC("-Warray-bounds")
inline QQmlListCompositor::iterator::iterator(
Range *range, int offset, Group group, int groupCount)
: range(range)
@@ -335,6 +339,7 @@ inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint
index[i] -= difference;
}
}
+QT_WARNING_POP // -Warray-bounds
inline QQmlListCompositor::insert_iterator::insert_iterator(
Range *range, int offset, Group group, int groupCount)
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index e4ef4e68ae..80b3f4b067 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -786,7 +786,8 @@ QObject *QuickTestResult::grabImage(QQuickItem *item)
if (item && item->window()) {
QQuickWindow *window = item->window();
QImage grabbed = window->grabWindow();
- QRectF rf(item->x(), item->y(), item->width(), item->height());
+ const auto dpi = grabbed.devicePixelRatio();
+ QRectF rf(item->x() * dpi, item->y() * dpi, item->width() * dpi, item->height() * dpi);
rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height()));
QObject *o = new QuickTestImageObject(grabbed.copy(rf.toAlignedRect()));
QQmlEngine::setContextForObject(o, qmlContext(this));
diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
index 9d1ff64f81..39f01bf9aa 100644
--- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
@@ -43,60 +43,60 @@ debugging features for Qt Quick make working with Qt Quick easier.
\section1 Qt Design Studio
-\l{Qt Design Studio} enables designing Qt Quick-based UIs using simple
-drag-n-drop gestures that most designers are familiar with. It offers UI
-elements from the Qt Quick and Qt Quick Controls modules, as well as
-integration for custom UI elements.
+\l{Qt Design Studio Manual}{Qt Design Studio} enables designing Qt Quick-based
+UIs using simple drag-n-drop gestures that most designers are familiar with.
+It offers UI elements from the Qt Quick and Qt Quick Controls modules, as well
+as integration for custom UI elements.
\section1 QML Debugger
-The \l{QML debugger} is a very useful utility that enables:
+The \l{Qt Creator: QML Debugger}{QML Debugger} is a very useful utility that
+enables:
\list
\li debugging JavaScript functions,
\li executing JavaScript expressions,
\li and inspecting QML properties.
\endlist
-The QML debugger is part of both \l{Qt Creator} and \l{Qt Design Studio}.
+The QML debugger is part of both \e{Qt Creator} and \e{Qt Design Studio}.
\section1 QML Profiler
-The \l{QML profiler} enables you to get necessary diagnostic information,
-allowing you to analyze the application code for performance issues. For
-example, too much JavaScript in each frame, long-running C++ functions, and
-so on.
+The \l{Qt Creator: QML Profiler}{QML Profiler} enables you to get necessary
+diagnostic information, allowing you to analyze the application code for
+performance issues. For example, too much JavaScript in each frame,
+long-running C++ functions, and so on.
-The profiler is part of both \l{Qt Creator} and \l{Qt Design Studio}.
+The profiler is part of both \e{Qt Creator} and \e{Qt Design Studio}.
\section1 QmlLive
-\l{QmlLive} is a 3rd party tool that offers a QML runtime capable of rendering
-changes to the code in realtime. It avoids the need to rebuild the
-application after every code change and install it on the target device.
-You can also extend it to build a custom runtime that suits your needs.
+\l{QmlLive Manual}{QmlLive} is a 3rd party tool that offers a QML runtime
+capable of rendering changes to the code in realtime. It avoids the need to
+rebuild the application after every code change and install it on the target
+device. You can also extend it to build a custom runtime that suits your needs.
\section1 GammaRay
-\l{GammaRay} is a useful utility that provides diagnostic information
-about your application. It is similar to the QML Profiler described in the
-earlier section, but offers a lot more. For example, the number of items or
-QObjects created, function calls made, time taken by each function call,
+\l{GammaRay Manual}{GammaRay} is a useful utility that provides diagnostic
+information about your application. It is similar to the QML Profiler described
+in the earlier section, but offers a lot more. For example, the number of items
+or QObjects created, function calls made, time taken by each function call,
property value introspection at runtime, and so on. Such information is very
handy, especially while debugging QML applications.
\section1 Squish
-\l{Squish} is a well-known testing tool that automates UI testing by recording
-your actions or running scripts. Once the tests are setup, UI tests are a lot
-easier to run.
+\l{Squish GUI Test Automation Tool}{Squish} is a well-known testing tool that
+automates UI testing by recording your actions or running scripts. Once the
+tests are setup, UI tests are a lot easier to run.
\section1 qmllint
-\l{qmllint} is a tool shipped with Qt, that verifies the syntatic validity of QML files.
-It also warns about some QML anti-patterns. If you want to disable a specific
-warning type, you can find the appropriate flag for doing so by passing \c{--help} on the command line.
-
-See the full documentation page of \l{qmllint} for details.
+\l{qtquick-tool-qmllint.html}{qmllint} is a tool shipped with Qt, that verifies
+the syntatic validity of QML files. It also warns about some QML anti-patterns.
+If you want to disable a specific warning type, you can find the appropriate
+flag for doing so by passing \c{--help} on the command line.
\section1 qmlformat
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 8dc9a9afe6..2c3c4423b9 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -43,6 +43,8 @@
#include "qquickwindow.h"
#include "qquickwindow_p.h"
#include "qquickevents_p_p.h"
+#include "qquickmousearea_p.h"
+#include "qquickdrag_p.h"
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquicktransition_p.h>
@@ -2553,6 +2555,23 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
+ bool receiverRelinquishGrab = false;
+
+ // Special case for MouseArea, try to guess what it does with the event
+ if (auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
+ bool preventStealing = mouseArea->preventStealing();
+ if (mouseArea->drag() && mouseArea->drag()->target())
+ preventStealing = true;
+ if (!preventStealing && receiverKeepsGrab) {
+ receiverRelinquishGrab = !receiverDisabled
+ || (QQuickDeliveryAgentPrivate::isMouseEvent(event)
+ && firstPoint.state() == QEventPoint::State::Pressed
+ && (receiver->acceptedMouseButtons() & static_cast<QMouseEvent *>(event)->button()));
+ if (receiverRelinquishGrab)
+ receiverKeepsGrab = false;
+ }
+ }
+
if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
localizedEvent->setAccepted(false);
@@ -2563,7 +2582,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
case QEventPoint::State::Pressed:
d->handlePressEvent(localizedEvent.data());
d->captureDelayedPress(receiver, event);
- stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
+ stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by handlePressEvent
break;
case QEventPoint::State::Released:
d->handleReleaseEvent(localizedEvent.data());
@@ -2580,7 +2599,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
event->setExclusiveGrabber(firstPoint, this);
}
- const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled;
+ const bool filtered = !receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled);
if (filtered) {
event->setAccepted(true);
}
@@ -2606,18 +2625,20 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickFlickable);
+ QPointerEvent *pointerEvent = e->isPointerEvent() ? static_cast<QPointerEvent *>(e) : nullptr;
- auto wantsPointerEvent_helper = [this, d, i, e]() {
- QPointerEvent *pe = static_cast<QPointerEvent *>(e);
- QQuickDeliveryAgentPrivate::localizePointerEvent(pe, this);
- const bool wants = d->wantsPointerEvent(pe);
+ auto wantsPointerEvent_helper = [this, d, i, pointerEvent]() {
+ Q_ASSERT(pointerEvent);
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, this);
+ const bool wants = d->wantsPointerEvent(pointerEvent);
// re-localize event back to \a i before returning
- QQuickDeliveryAgentPrivate::localizePointerEvent(pe, i);
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, i);
return wants;
};
if (!isVisible() || !isEnabled() || !isInteractive() ||
- (e->isPointerEvent() && !wantsPointerEvent_helper())) {
+ (pointerEvent && isMoving() && pointerEvent->isBeginEvent()) ||
+ (pointerEvent && !wantsPointerEvent_helper())) {
d->cancelInteraction();
return QQuickItem::childMouseEventFilter(i, e);
}
@@ -2629,8 +2650,8 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
qCDebug(lcFilter) << "filtering UngrabMouse" << spe->points().first() << "for" << i << "grabber is" << grabber;
if (grabber != this)
mouseUngrabEvent(); // A child has been ungrabbed
- } else if (e->isPointerEvent()) {
- return filterPointerEvent(i, static_cast<QPointerEvent *>(e));
+ } else if (pointerEvent) {
+ return filterPointerEvent(i, pointerEvent);
}
return QQuickItem::childMouseEventFilter(i, e);
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 5c4e4d7018..e0adac3337 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -208,6 +208,8 @@ public:
void updateHeader() override;
void updateFooter() override;
+ void initializeComponentItem(QQuickItem *item) const override;
+
void changedVisibleIndex(int newIndex) override;
void initializeCurrentItem() override;
@@ -853,6 +855,14 @@ void QQuickGridViewPrivate::updateFooter()
emit q->footerItemChanged();
}
+void QQuickGridViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
+ qmlAttachedPropertiesObject<QQuickGridView>(item));
+ if (attached)
+ attached->setView(const_cast<QQuickGridView*>(q_func()));
+}
+
void QQuickGridViewPrivate::updateHeader()
{
Q_Q(QQuickGridView);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 8102a473db..4ecf793dde 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4087,8 +4087,9 @@ void QQuickItem::mouseReleaseEvent(QMouseEvent *event)
\input item.qdocinc accepting-events
*/
-void QQuickItem::mouseDoubleClickEvent(QMouseEvent *)
+void QQuickItem::mouseDoubleClickEvent(QMouseEvent *event)
{
+ event->ignore();
}
/*!
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 39e1f42fe5..943d24b32c 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -57,8 +57,6 @@ FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemVie
, view(v)
, attached(attached)
{
- if (attached) // can be null for default components (see createComponentItem)
- attached->setView(view);
}
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
@@ -2502,12 +2500,29 @@ QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component,
item->setZ(zValue);
QQml_setParent_noEvent(item, q->contentItem());
item->setParentItem(q->contentItem());
+
+ initializeComponentItem(item);
}
if (component)
component->completeCreate();
return item;
}
+/*!
+ \internal
+
+ Allows derived classes to do any initialization required for \a item
+ before completeCreate() is called on it. For example, any attached
+ properties required by the item can be set.
+
+ This is similar to initItem(), but as that has logic specific to
+ delegate items, we use a separate function for non-delegates.
+*/
+void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ Q_UNUSED(item);
+}
+
void QQuickItemViewPrivate::updateTrackedItem()
{
Q_Q(QQuickItemView);
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index d3b12268aa..d48e4160ac 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -178,6 +178,7 @@ public:
QQuickItem *createHighlightItem() const;
QQuickItem *createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault = false) const;
+ virtual void initializeComponentItem(QQuickItem *) const;
void updateCurrent(int modelIndex);
void updateTrackedItem();
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 7ddda6196f..868d70b3fe 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -130,6 +130,8 @@ public:
bool hasStickyHeader() const override;
bool hasStickyFooter() const override;
+ void initializeComponentItem(QQuickItem *item) const override;
+
void changedVisibleIndex(int newIndex) override;
void initializeCurrentItem() override;
@@ -1575,6 +1577,14 @@ bool QQuickListViewPrivate::hasStickyFooter() const
return footer && footerPositioning != QQuickListView::InlineFooter;
}
+void QQuickListViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
+ qmlAttachedPropertiesObject<QQuickListView>(item));
+ if (attached) // can be null for default components (see createComponentItem)
+ attached->setView(const_cast<QQuickListView*>(q_func()));
+}
+
void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeometry)
{
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index f739474908..678b38e1d8 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1592,15 +1592,20 @@ bool QQuickTextEdit::selectByMouse() const
void QQuickTextEdit::setSelectByMouse(bool on)
{
Q_D(QQuickTextEdit);
- if (d->selectByMouse != on) {
- d->selectByMouse = on;
- setKeepMouseGrab(on);
- if (on)
- d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
- else
- d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
- emit selectByMouseChanged(on);
- }
+ if (d->selectByMouse == on)
+ return;
+
+ d->selectByMouse = on;
+ setKeepMouseGrab(on);
+ if (on)
+ d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
+ else
+ d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
+
+#if QT_CONFIG(cursor)
+ d->updateMouseCursorShape();
+#endif
+ emit selectByMouseChanged(on);
}
/*!
@@ -1663,6 +1668,9 @@ void QQuickTextEdit::setReadOnly(bool r)
#if QT_CONFIG(im)
updateInputMethod(Qt::ImEnabled);
#endif
+#if QT_CONFIG(cursor)
+ d->updateMouseCursorShape();
+#endif
q_canPasteChanged();
emit readOnlyChanged(r);
if (!d->selectByKeyboardSet)
@@ -2249,6 +2257,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
if (inView) {
if (!engine.hasContents()) {
+ if (node && !node->parent())
+ d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
node = d->createTextNode();
updateNodeTransform(node, nodeOffset);
nodeStart = block.position();
@@ -2450,7 +2460,7 @@ void QQuickTextEditPrivate::init()
updateDefaultTextOption();
q->updateSize();
#if QT_CONFIG(cursor)
- q->setCursor(Qt::IBeamCursor);
+ updateMouseCursorShape();
#endif
}
@@ -2734,9 +2744,8 @@ void QQuickTextEdit::q_linkHovered(const QString &link)
emit linkHovered(link);
#if QT_CONFIG(cursor)
if (link.isEmpty()) {
- setCursor(d->cursorToRestoreAfterHover);
+ d->updateMouseCursorShape();
} else if (cursor().shape() != Qt::PointingHandCursor) {
- d->cursorToRestoreAfterHover = cursor().shape();
setCursor(Qt::PointingHandCursor);
}
#endif
@@ -2747,9 +2756,8 @@ void QQuickTextEdit::q_markerHovered(bool hovered)
Q_D(QQuickTextEdit);
#if QT_CONFIG(cursor)
if (!hovered) {
- setCursor(d->cursorToRestoreAfterHover);
+ d->updateMouseCursorShape();
} else if (cursor().shape() != Qt::PointingHandCursor) {
- d->cursorToRestoreAfterHover = cursor().shape();
setCursor(Qt::PointingHandCursor);
}
#endif
@@ -3020,6 +3028,14 @@ bool QQuickTextEditPrivate::isLinkHoveredConnected()
IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (const QString &));
}
+#if QT_CONFIG(cursor)
+void QQuickTextEditPrivate::updateMouseCursorShape()
+{
+ Q_Q(QQuickTextEdit);
+ q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
+}
+#endif
+
/*!
\qmlsignal QtQuick::TextEdit::linkHovered(string link)
\since 5.2
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index 2381b46a05..4ec2752eb5 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -147,6 +147,10 @@ public:
Qt::LayoutDirection textDirection(const QString &text) const;
bool isLinkHoveredConnected();
+#if QT_CONFIG(cursor)
+ void updateMouseCursorShape();
+#endif
+
void setNativeCursorEnabled(bool) {}
void handleFocusEvent(QFocusEvent *event);
void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos);
@@ -211,7 +215,6 @@ public:
Qt::InputMethodHints inputMethodHints;
#endif
UpdateType updateType;
- Qt::CursorShape cursorToRestoreAfterHover = Qt::IBeamCursor;
bool dirty : 1;
bool richText : 1;
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 72fe2a56fe..cb50462761 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -517,8 +517,16 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
}
/*!
- \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment
+ \readonly
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+*/
+/*!
+ \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::verticalAlignment
Sets the horizontal alignment of the text within the TextInput item's
@@ -541,7 +549,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
\c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
- of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+ of TextInput, use the read-only property \l effectiveHorizontalAlignment.
*/
QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
{
@@ -861,6 +869,7 @@ void QQuickTextInput::setCursorPosition(int cp)
/*!
\qmlproperty rectangle QtQuick::TextInput::cursorRectangle
+ \readonly
The rectangle where the standard text cursor is rendered within the text input. Read only.
@@ -902,6 +911,7 @@ QRectF QQuickTextInput::cursorRectangle() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionEnd, cursorPosition, selectedText
*/
int QQuickTextInput::selectionStart() const
@@ -917,6 +927,7 @@ int QQuickTextInput::selectionStart() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionStart, cursorPosition, selectedText
*/
int QQuickTextInput::selectionEnd() const
@@ -947,6 +958,7 @@ void QQuickTextInput::select(int start, int end)
/*!
\qmlproperty string QtQuick::TextInput::selectedText
+ \readonly
This read-only property provides the text currently selected in the
text input.
@@ -2470,6 +2482,7 @@ void QQuickTextInput::setPersistentSelection(bool on)
/*!
\qmlproperty bool QtQuick::TextInput::canPaste
+ \readonly
Returns true if the TextInput is writable and the content of the clipboard is
suitable for pasting into the TextInput.
@@ -2491,6 +2504,7 @@ bool QQuickTextInput::canPaste() const
/*!
\qmlproperty bool QtQuick::TextInput::canUndo
+ \readonly
Returns true if the TextInput is writable and there are previous operations
that can be undone.
@@ -2504,6 +2518,7 @@ bool QQuickTextInput::canUndo() const
/*!
\qmlproperty bool QtQuick::TextInput::canRedo
+ \readonly
Returns true if the TextInput is writable and there are \l {undo}{undone}
operations that can be redone.
@@ -2517,6 +2532,7 @@ bool QQuickTextInput::canRedo() const
/*!
\qmlproperty real QtQuick::TextInput::contentWidth
+ \readonly
Returns the width of the text, including the width past the width
which is covered due to insufficient wrapping if \l wrapMode is set.
@@ -2530,6 +2546,7 @@ qreal QQuickTextInput::contentWidth() const
/*!
\qmlproperty real QtQuick::TextInput::contentHeight
+ \readonly
Returns the height of the text, including the height past the height
that is covered if the text does not fit within the set height.
@@ -2693,7 +2710,7 @@ void QQuickTextInput::focusOutEvent(QFocusEvent *event)
/*!
\qmlproperty bool QtQuick::TextInput::inputMethodComposing
-
+ \readonly
This property holds whether the TextInput has partial text input from an
input method.
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 06ea705c22..dfba7379d0 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -900,7 +900,7 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
// The shader manager is shared between renderers (think for example Item
// layers that create a new Renderer each) with the same rendercontext (and
// so same QRhi).
- m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
+ m_shaderManager = ctx->findChild<ShaderManager *>(QString(), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 83ec6e09a3..757f231872 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -274,7 +274,8 @@ bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(RenderState &state,
ps->srcColor = GraphicsPipelineState::ConstantColor;
ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
- QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ QVector4D color = mat->color();
+
// if (useSRGB())
// color = qt_sRGB_to_linear_RGB(color);
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index ed34fbe8cf..6adef81ebd 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -1536,8 +1536,7 @@ void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event)
case QEvent::MouseButtonDblClick:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
event->button(), event->buttons());
- if (allowDoubleClick)
- deliverPointerEvent(event);
+ deliverPointerEvent(event);
break;
case QEvent::MouseMove: {
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
@@ -2017,11 +2016,9 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
// Let the Item's handlers (if any) have the event first.
// However, double click should never be delivered to handlers.
- if (pointerEvent->type() != QEvent::MouseButtonDblClick) {
- bool wasAccepted = pointerEvent->allPointsAccepted();
+ if (pointerEvent->type() != QEvent::MouseButtonDblClick)
itemPrivate->handlePointerEvent(pointerEvent);
- allowDoubleClick = wasAccepted || !(isMouse && pointerEvent->isBeginEvent() && pointerEvent->allPointsAccepted());
- }
+
if (handlersOnly)
return;
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index b962864103..153fdf765e 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -118,7 +118,6 @@ public:
#endif
uchar compressedTouchCount = 0;
bool allowChildEventFiltering = true;
- bool allowDoubleClick = true;
bool frameSynchronousHoverEnabled = true;
bool hoveredLeafItemFound = false;
diff --git a/src/quickcontrols2/doc/src/qtquickcontrols2-delegates.qdoc b/src/quickcontrols2/doc/src/qtquickcontrols2-delegates.qdoc
index bea87abf05..18082559a8 100644
--- a/src/quickcontrols2/doc/src/qtquickcontrols2-delegates.qdoc
+++ b/src/quickcontrols2/doc/src/qtquickcontrols2-delegates.qdoc
@@ -40,6 +40,16 @@
sections offer guidelines for choosing the appropriate type of delegate,
depending on the use case.
+ \section1 CheckDelegate Control
+
+ \image qtquickcontrols2-checkdelegate.gif
+
+ \l CheckDelegate presents a checkable control that can be toggled on
+ (checked) or off (unchecked). Check delegates are typically used to
+ select one or more options from a set of options.
+
+ \b {See also} \l {CheckBox Control}.
+
\section1 ItemDelegate Control
\image qtquickcontrols2-itemdelegate.gif
@@ -72,6 +82,15 @@
\b {See also} \l {Switch Control}.
+ \section1 TreeViewDelegate Control
+
+ \image qtquickcontrols2-treeviewdelegate.png
+
+ \l A TreeViewDelegate is a delegate that can be assigned to the delegate property
+ of a TreeView.
+
+ \b {See also} \l {TreeView Control}.
+
\section1 Related Information
\list
\li \l {Qt Quick Controls Guidelines}
diff --git a/src/quickcontrols2impl/qquickplatformtheme.cpp b/src/quickcontrols2impl/qquickplatformtheme.cpp
index cc2aea3fe0..cc2d1abd2b 100644
--- a/src/quickcontrols2impl/qquickplatformtheme.cpp
+++ b/src/quickcontrols2impl/qquickplatformtheme.cpp
@@ -56,12 +56,22 @@ QQuickPlatformTheme::QQuickPlatformTheme(QObject *parent) :
QVariant QQuickPlatformTheme::themeHint(QPlatformTheme::ThemeHint themeHint) const
{
+ return getThemeHint(themeHint);
+}
+
+/*!
+ \internal
+
+ This is static to allow us to call it from C++, as we're only available as a singleton in QML.
+*/
+QVariant QQuickPlatformTheme::getThemeHint(QPlatformTheme::ThemeHint themeHint)
+{
// Allow tests to force some theme hint values, otherwise they get very messy and difficult to understand.
switch (themeHint) {
case QPlatformTheme::ShowDirectoriesFirst: {
const QVariant showDirsFirst = qEnvironmentVariable("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST");
if (showDirsFirst.isValid() && showDirsFirst.canConvert<bool>())
- return showDirsFirst;
+ return showDirsFirst.toBool();
break;
}
default:
diff --git a/src/quickcontrols2impl/qquickplatformtheme_p.h b/src/quickcontrols2impl/qquickplatformtheme_p.h
index 3a7bed128f..2c08e1b0c3 100644
--- a/src/quickcontrols2impl/qquickplatformtheme_p.h
+++ b/src/quickcontrols2impl/qquickplatformtheme_p.h
@@ -72,6 +72,8 @@ public:
explicit QQuickPlatformTheme(QObject *parent = nullptr);
Q_INVOKABLE QVariant themeHint(QPlatformTheme::ThemeHint themeHint) const;
+
+ static QVariant getThemeHint(QPlatformTheme::ThemeHint themeHint);
};
QT_END_NAMESPACE
diff --git a/src/quickcontrolstestutils/controlstestutils.cpp b/src/quickcontrolstestutils/controlstestutils.cpp
index a1e64784c4..ec3748fed4 100644
--- a/src/quickcontrolstestutils/controlstestutils.cpp
+++ b/src/quickcontrolstestutils/controlstestutils.cpp
@@ -34,8 +34,8 @@
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
QQuickControlsTestUtils::QQuickControlsApplicationHelper::QQuickControlsApplicationHelper(QQmlDataTest *testCase,
- const QString &testFilePath, const QStringList &qmlImportPaths, const QVariantMap &initialProperties)
- : QQuickApplicationHelper(testCase, testFilePath, qmlImportPaths, initialProperties)
+ const QString &testFilePath, const QVariantMap &initialProperties, const QStringList &qmlImportPaths)
+ : QQuickApplicationHelper(testCase, testFilePath, initialProperties, qmlImportPaths)
{
if (ready)
appWindow = qobject_cast<QQuickApplicationWindow*>(cleanup.data());
diff --git a/src/quickcontrolstestutils/controlstestutils_p.h b/src/quickcontrolstestutils/controlstestutils_p.h
index 35e61aabda..4d4f0432b0 100644
--- a/src/quickcontrolstestutils/controlstestutils_p.h
+++ b/src/quickcontrolstestutils/controlstestutils_p.h
@@ -54,15 +54,15 @@ namespace QQuickControlsTestUtils
{
public:
QQuickControlsApplicationHelper(QQmlDataTest *testCase, const QString &testFilePath,
- const QStringList &qmlImportPaths = {},
- const QVariantMap &initialProperties = {});
+ const QVariantMap &initialProperties = {},
+ const QStringList &qmlImportPaths = {});
QQuickApplicationWindow *appWindow = nullptr;
};
struct QQuickStyleHelper
{
- bool updateStyle(const QString &style);
+ [[nodiscard]] bool updateStyle(const QString &style);
QString currentStyle;
QScopedPointer<QQmlEngine> engine;
diff --git a/src/quickcontrolstestutils/dialogstestutils_p.h b/src/quickcontrolstestutils/dialogstestutils_p.h
index f358c1316f..15549ae9f9 100644
--- a/src/quickcontrolstestutils/dialogstestutils_p.h
+++ b/src/quickcontrolstestutils/dialogstestutils_p.h
@@ -97,7 +97,7 @@ class DialogTestHelper
public:
DialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath,
const QStringList &qmlImportPaths = {}, const QVariantMap &initialProperties = {}) :
- appHelper(testCase, testFilePath, qmlImportPaths, initialProperties)
+ appHelper(testCase, testFilePath, initialProperties, qmlImportPaths)
{
if (!appHelper.ready)
return;
diff --git a/src/quickdialogs2/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
index 7812ebb818..70bf1baab3 100644
--- a/src/quickdialogs2/quickdialogs2/CMakeLists.txt
+++ b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
@@ -34,6 +34,7 @@ qt_internal_add_qml_module(QuickDialogs2
Qt::GuiPrivate
Qt::QmlPrivate
Qt::QuickPrivate
+ Qt::QuickControls2Impl
Qt::QuickDialogs2Utils
Qt::QuickDialogs2UtilsPrivate
Qt::QuickDialogs2QuickImpl
diff --git a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
index 12445c83b7..7a203d2b24 100644
--- a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
@@ -469,11 +469,22 @@ bool QQuickAbstractDialog::useNativeDialog() const
return true;
}
+/*!
+ \internal
+
+ Called at the end of \l create().
+*/
void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog)
{
Q_UNUSED(dialog);
}
+/*!
+ \internal
+
+ Called by \l open(), after the call to \l create() and before
+ the handle/helper's \c show function is called.
+*/
void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog)
{
Q_UNUSED(dialog);
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
index 8390026dd6..612d6dec44 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
@@ -158,10 +158,12 @@ void QQuickFileDialog::setFileMode(FileMode mode)
/*!
\qmlproperty url QtQuick.Dialogs::FileDialog::selectedFile
- \readonly
This property holds the last file that was selected in the dialog.
+ It can be set to control the file that is selected when the dialog is
+ opened.
+
If there are multiple selected files, this property refers to the first
file.
@@ -176,6 +178,11 @@ QUrl QQuickFileDialog::selectedFile() const
return addDefaultSuffix(m_selectedFiles.value(0));
}
+void QQuickFileDialog::setSelectedFile(const QUrl &selectedFile)
+{
+ setSelectedFiles({ selectedFile });
+}
+
/*!
\qmlproperty list<url> QtQuick.Dialogs::FileDialog::selectedFiles
@@ -194,11 +201,14 @@ QList<QUrl> QQuickFileDialog::selectedFiles() const
void QQuickFileDialog::setSelectedFiles(const QList<QUrl> &selectedFiles)
{
+ qCDebug(lcFileDialog) << "setSelectedFiles called with" << selectedFiles;
if (m_selectedFiles == selectedFiles)
return;
- bool firstChanged = m_selectedFiles.value(0) != selectedFiles.value(0);
+ const auto newFirstSelectedFile = selectedFiles.value(0);
+ const bool firstChanged = m_selectedFiles.value(0) != newFirstSelectedFile;
m_selectedFiles = selectedFiles;
+ m_options->setInitiallySelectedFiles(m_selectedFiles);
if (firstChanged) {
emit selectedFileChanged();
emit currentFileChanged();
@@ -534,9 +544,14 @@ void QQuickFileDialog::onCreate(QPlatformDialogHelper *dialog)
connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFileDialog::currentFolderChanged);
fileDialog->setOptions(m_options);
- // Need to call this manually once on creation because QPlatformFileDialogHelper::currentChanged
- // has already been emitted by this point (because of QQuickFileDialogImplPrivate::updateSelectedFile).
- setSelectedFiles(fileDialog->selectedFiles());
+ // If the user didn't set an initial selectedFile, ensure that we are synced
+ // with the underlying dialog in case it has set an initially selected file
+ // (as QQuickFileDialogImplPrivate::updateSelectedFile does).
+ if (m_options->initiallySelectedFiles().isEmpty()) {
+ const auto selectedFiles = fileDialog->selectedFiles();
+ if (!selectedFiles.isEmpty())
+ setSelectedFiles(selectedFiles);
+ }
}
}
@@ -556,10 +571,18 @@ void QQuickFileDialog::onShow(QPlatformDialogHelper *dialog)
connect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update);
fileDialog->selectNameFilter(filter);
- const QUrl initialDir = m_options->initialDirectory();
- // If it's not valid, or it's a file and not a directory, we shouldn't set it.
- if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists())
- fileDialog->setDirectory(m_options->initialDirectory());
+ // If both selectedFile and currentFolder are set, prefer the former.
+ if (!m_options->initiallySelectedFiles().isEmpty()) {
+ // The user set an initial selectedFile.
+ const QUrl selectedFile = m_options->initiallySelectedFiles().first();
+ fileDialog->selectFile(selectedFile);
+ } else {
+ // The user set an initial currentFolder.
+ const QUrl initialDir = m_options->initialDirectory();
+ // If it's not valid, or it's a file and not a directory, we shouldn't set it.
+ if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists())
+ fileDialog->setDirectory(m_options->initialDirectory());
+ }
}
QQuickAbstractDialog::onShow(dialog);
}
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
index 1d1a0e82d1..b9d9b878a8 100644
--- a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
@@ -64,7 +64,7 @@ class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFileDialog : public QQuickAbstractDia
{
Q_OBJECT
Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode NOTIFY fileModeChanged FINAL)
- Q_PROPERTY(QUrl selectedFile READ selectedFile NOTIFY selectedFileChanged FINAL)
+ Q_PROPERTY(QUrl selectedFile READ selectedFile WRITE setSelectedFile NOTIFY selectedFileChanged FINAL)
Q_PROPERTY(QList<QUrl> selectedFiles READ selectedFiles NOTIFY selectedFilesChanged FINAL)
Q_PROPERTY(QUrl currentFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged FINAL)
Q_PROPERTY(QList<QUrl> currentFiles READ currentFiles WRITE setCurrentFiles NOTIFY currentFilesChanged FINAL)
@@ -94,6 +94,7 @@ public:
void setFileMode(FileMode fileMode);
QUrl selectedFile() const;
+ void setSelectedFile(const QUrl &selectedFile);
QList<QUrl> selectedFiles() const;
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt b/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt
index 23c7728149..d4623edeaf 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt
+++ b/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt
@@ -97,6 +97,7 @@ qt_internal_add_qml_module(QuickDialogs2QuickImpl
Qt::QuickPrivate
Qt::QuickTemplates2
Qt::QuickTemplates2Private
+ Qt::QuickControls2ImplPrivate
Qt::QuickDialogs2Utils
Qt::QuickDialogs2UtilsPrivate
PUBLIC_LIBRARIES
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
index 00f34f8cc1..7fcb2b9fc5 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
@@ -43,19 +43,24 @@
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
-#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
+#include <QtQuickControls2Impl/private/qquickplatformtheme_p.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+
#include "qquickfiledialogdelegate_p.h"
#include "qquickfolderbreadcrumbbar_p.h"
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcCurrentFolder, "qt.quick.dialogs.quickfiledialogimpl.currentFolder")
+Q_LOGGING_CATEGORY(lcSelectedFile, "qt.quick.dialogs.quickfiledialogimpl.selectedFile")
Q_LOGGING_CATEGORY(lcUpdateSelectedFile, "qt.quick.dialogs.quickfiledialogimpl.updateSelectedFile")
Q_LOGGING_CATEGORY(lcOptions, "qt.quick.dialogs.quickfiledialogimpl.options")
Q_LOGGING_CATEGORY(lcNameFilters, "qt.quick.dialogs.quickfiledialogimpl.namefilters")
Q_LOGGING_CATEGORY(lcAttachedNameFilters, "qt.quick.dialogs.quickfiledialogimplattached.namefilters")
+Q_LOGGING_CATEGORY(lcAttachedCurrentIndex, "qt.quick.dialogs.quickfiledialogimplattached.currentIndex")
QQuickFileDialogImplPrivate::QQuickFileDialogImplPrivate()
{
@@ -102,10 +107,14 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat
if (!attached || !attached->fileDialogListView())
return;
+ qCDebug(lcUpdateSelectedFile) << "updateSelectedFile called with oldFolderPath" << oldFolderPath;
+
QString newSelectedFilePath;
- int newSelectedFileIndex = 0;
+ int newSelectedFileIndex = -1;
const QString newFolderPath = QQmlFile::urlToLocalFileOrQrc(currentFolder);
if (!oldFolderPath.isEmpty() && !newFolderPath.isEmpty()) {
+ // TODO: Add another platform theme hint for this behavior too, as e.g. macOS
+ // doesn't do it this way.
// If the user went up a directory (or several), we should set
// selectedFile to be the directory that we were in (or
// its closest ancestor that is a child of the new directory).
@@ -142,22 +151,68 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat
// so we can't use that. QQuickFolderListModel uses threads to fetch its data,
// so should be considered asynchronous. We might be able to use it, but it would
// complicate the code even more...
- QDir newFolderDir(newFolderPath);
+ const QDir newFolderDir(newFolderPath);
if (newFolderDir.exists()) {
- const QFileInfoList files = newFolderDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::DirsFirst);
- if (!files.isEmpty())
+ const QFileInfoList files = fileList(newFolderDir);
+ if (!files.isEmpty()) {
newSelectedFilePath = files.first().absoluteFilePath();
+ newSelectedFileIndex = 0;
+ }
}
}
const QUrl newSelectedFileUrl = QUrl::fromLocalFile(newSelectedFilePath);
qCDebug(lcUpdateSelectedFile) << "updateSelectedFile is setting selectedFile to" << newSelectedFileUrl;
q->setSelectedFile(newSelectedFileUrl);
- if (!newSelectedFilePath.isEmpty()) {
- attached->fileDialogListView()->setCurrentIndex(newSelectedFileIndex);
- if (QQuickItem *currentItem = attached->fileDialogListView()->currentItem())
- currentItem->forceActiveFocus();
- }
+ updateFileDialogListViewCurrentIndex(newSelectedFileIndex);
+}
+
+QFileInfoList QQuickFileDialogImplPrivate::fileList(const QDir &dir)
+{
+ QDir::SortFlags sortFlags = QDir::NoSort;
+ if (QQuickPlatformTheme::getThemeHint(QPlatformTheme::ShowDirectoriesFirst).toBool())
+ sortFlags = QDir::DirsFirst;
+ return dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, sortFlags);
+}
+
+/*!
+ \internal
+
+ Returns the index of \a filePath if the contents of \a dir were displayed
+ in fileDialogListView.
+*/
+int QQuickFileDialogImplPrivate::indexOfFileInFileDialogListView(const QString &filePath) const
+{
+ const QFileInfo newSelectedFileInfo(filePath);
+ const QFileInfoList dirs = fileList(newSelectedFileInfo.absoluteDir());
+ return dirs.indexOf(newSelectedFileInfo);
+}
+
+/*!
+ \internal
+
+ Sets the currentIndex of fileDialogListView to \a newCurrentIndex and gives
+ focus to the current item.
+*/
+void QQuickFileDialogImplPrivate::updateFileDialogListViewCurrentIndex(int newCurrentIndex)
+{
+ qCDebug(lcSelectedFile) << "updateFileDialogListViewCurrentIndex called with newCurrentIndex" << newCurrentIndex;
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ Q_ASSERT(attached);
+ Q_ASSERT(attached->fileDialogListView());
+
+ // We were likely trying to set an index for a file that the ListView hadn't loaded yet.
+ // For now we just select the first item, but this needs to be fixed properly: QTBUG-103547
+ if (newCurrentIndex >= attached->fileDialogListView()->count())
+ newCurrentIndex = 0;
+
+ // We block signals from ListView because we don't want fileDialogListViewCurrentIndexChanged
+ // to be called, as the file it gets from the delegate will not be up-to-date (but most
+ // importantly because we already just set the selected file).
+ QSignalBlocker blocker(attached->fileDialogListView());
+ attached->fileDialogListView()->setCurrentIndex(newCurrentIndex);
+ if (QQuickItem *currentItem = attached->fileDialogListView()->currentItem())
+ currentItem->forceActiveFocus();
}
void QQuickFileDialogImplPrivate::handleAccept()
@@ -201,7 +256,7 @@ QUrl QQuickFileDialogImpl::currentFolder() const
return d->currentFolder;
}
-void QQuickFileDialogImpl::setCurrentFolder(const QUrl &currentFolder)
+void QQuickFileDialogImpl::setCurrentFolder(const QUrl &currentFolder, SetReason setReason)
{
qCDebug(lcCurrentFolder) << "setCurrentFolder called with" << currentFolder;
Q_D(QQuickFileDialogImpl);
@@ -211,8 +266,12 @@ void QQuickFileDialogImpl::setCurrentFolder(const QUrl &currentFolder)
const QString oldFolderPath = QQmlFile::urlToLocalFileOrQrc(d->currentFolder);
d->currentFolder = currentFolder;
- // Since the directory changed, the old file can no longer be selected.
- d->updateSelectedFile(oldFolderPath);
+ // Don't update the selectedFile if it's an Internal set, as that
+ // means that the user just set selectedFile, and we're being called as a result of that.
+ if (setReason == SetReason::External) {
+ // Since the directory changed, the old file can no longer be selected.
+ d->updateSelectedFile(oldFolderPath);
+ }
emit currentFolderChanged(d->currentFolder);
}
@@ -222,8 +281,16 @@ QUrl QQuickFileDialogImpl::selectedFile() const
return d->selectedFile;
}
+/*!
+ \internal
+
+ This is mostly called as a result of user interaction, but is also
+ called (indirectly) by QQuickFileDialog::onShow when the user set an initial
+ selectedFile.
+*/
void QQuickFileDialogImpl::setSelectedFile(const QUrl &selectedFile)
{
+ qCDebug(lcSelectedFile) << "setSelectedFile called with" << selectedFile;
Q_D(QQuickFileDialogImpl);
if (selectedFile == d->selectedFile)
return;
@@ -233,6 +300,13 @@ void QQuickFileDialogImpl::setSelectedFile(const QUrl &selectedFile)
emit selectedFileChanged(d->selectedFile);
}
+void QQuickFileDialogImpl::setInitialSelectedFile(const QUrl &file)
+{
+ Q_D(QQuickFileDialogImpl);
+ setSelectedFile(file);
+ d->setCurrentIndexToInitiallySelectedFile = true;
+}
+
QSharedPointer<QFileDialogOptions> QQuickFileDialogImpl::options() const
{
Q_D(const QQuickFileDialogImpl);
@@ -418,7 +492,27 @@ void QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged(
if (!fileDialogDelegate)
return;
- fileDialogImpl->setSelectedFile(fileDialogDelegate->file());
+ const QQuickItemViewPrivate::MovementReason moveReason = QQuickItemViewPrivate::get(fileDialogListView)->moveReason;
+ qCDebug(lcAttachedCurrentIndex).nospace() << "fileDialogListView currentIndex changed to " << fileDialogListView->currentIndex()
+ << " with moveReason " << moveReason
+ << "; the file at that index is " << fileDialogDelegate->file();
+
+ // Only update selectedFile if the currentIndex changed as a result of user interaction;
+ // things like model changes (i.e. QQuickItemViewPrivate::applyModelChanges() calling
+ // QQuickItemViewPrivate::updateCurrent as a result of us changing the directory on the FolderListModel)
+ // shouldn't cause the selectedFile to change.
+ auto fileDialogImplPrivate = QQuickFileDialogImplPrivate::get(fileDialogImpl);
+ if (moveReason != QQuickItemViewPrivate::Other) {
+ fileDialogImpl->setSelectedFile(fileDialogDelegate->file());
+ } else if (fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile) {
+ // When setting selectedFile before opening the FileDialog,
+ // we need to ensure that the currentIndex is correct, because the initial change
+ // in directory will cause the underyling FolderListModel to change its folder property,
+ // which in turn resets the fileDialogListView's currentIndex to 0.
+ fileDialogImplPrivate->updateFileDialogListViewCurrentIndex(
+ fileDialogImplPrivate->indexOfFileInFileDialogListView(fileDialogImplPrivate->selectedFile.toLocalFile()));
+ fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile = false;
+ }
}
QQuickFileDialogImplAttached::QQuickFileDialogImplAttached(QObject *parent)
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h
index c2bcee0f65..5737f5f8f5 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h
@@ -85,11 +85,19 @@ public:
static QQuickFileDialogImplAttached *qmlAttachedProperties(QObject *object);
+ enum class SetReason {
+ // Either user interaction or e.g. a change in ListView's currentIndex after changing its model.
+ External,
+ // As a result of the user setting an initial selectedFile.
+ Internal
+ };
+
QUrl currentFolder() const;
- void setCurrentFolder(const QUrl &currentFolder);
+ void setCurrentFolder(const QUrl &currentFolder, SetReason setReason = SetReason::External);
QUrl selectedFile() const;
void setSelectedFile(const QUrl &file);
+ void setInitialSelectedFile(const QUrl &file);
QSharedPointer<QFileDialogOptions> options() const;
void setOptions(const QSharedPointer<QFileDialogOptions> &options);
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h
index d2336e9aa1..7050a9e085 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h
@@ -79,6 +79,9 @@ public:
void updateEnabled();
void updateSelectedFile(const QString &oldFolderPath);
+ static QFileInfoList fileList(const QDir &dir);
+ int indexOfFileInFileDialogListView(const QString &filePath) const;
+ void updateFileDialogListViewCurrentIndex(int newCurrentIndex);
void handleAccept() override;
void handleClick(QQuickAbstractButton *button) override;
@@ -90,6 +93,7 @@ public:
mutable QQuickFileNameFilter *selectedNameFilter = nullptr;
QString acceptLabel;
QString rejectLabel;
+ bool setCurrentIndexToInitiallySelectedFile = false;
};
class QQuickFileDialogImplAttachedPrivate : public QObjectPrivate
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp
index ee015290df..491719aa95 100644
--- a/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp
@@ -106,11 +106,6 @@ QQuickPlatformFileDialog::QQuickPlatformFileDialog(QObject *parent)
connect(m_dialog, &QQuickFileDialogImpl::selectedFileChanged, this, &QQuickPlatformFileDialog::currentChanged);
connect(m_dialog, &QQuickFileDialogImpl::currentFolderChanged, this, &QQuickPlatformFileDialog::directoryEntered);
connect(m_dialog, &QQuickFileDialogImpl::filterSelected, this, &QQuickPlatformFileDialog::filterSelected);
-
- // We would do this in QQuickFileDialogImpl, but we need to ensure that folderChanged()
- // is connected to directoryEntered() before setting it to ensure that the QQuickFileDialog is notified.
- if (m_dialog->currentFolder().isEmpty())
- m_dialog->setCurrentFolder(QUrl::fromLocalFile(QDir().absolutePath()));
}
bool QQuickPlatformFileDialog::isValid() const
@@ -144,12 +139,28 @@ void QQuickPlatformFileDialog::selectFile(const QUrl &file)
if (!m_dialog)
return;
- m_dialog->setSelectedFile(file);
+ if (m_dialog->isVisible()) {
+ qWarning() << "Cannot set an initial selectedFile while FileDialog is open";
+ return;
+ }
+
+ // Since we're only called once each time the FileDialog is shown,
+ // we call setInitialSelectedFile here, which will ensure that
+ // the first currentIndex change (to 0, as a result of the ListView's model changing
+ // as a result of the FolderListModel directory change) is effectively
+ // ignored and the correct index for the initial selectedFile is maintained.
+ const QUrl fileDirUrl = QUrl::fromLocalFile(QFileInfo(file.toLocalFile()).dir().absolutePath());
+ qCDebug(lcQuickPlatformFileDialog) << "setting initial currentFolder to" << fileDirUrl << "and selectedFile to" << file;
+ m_dialog->setCurrentFolder(fileDirUrl, QQuickFileDialogImpl::SetReason::Internal);
+ m_dialog->setInitialSelectedFile(file);
}
+// TODO: support for multiple selected files
QList<QUrl> QQuickPlatformFileDialog::selectedFiles() const
{
- // TODO: support for multiple selected files
+ if (m_dialog->selectedFile().isEmpty())
+ return {};
+
return { m_dialog->selectedFile() };
}
@@ -178,6 +189,12 @@ void QQuickPlatformFileDialog::exec()
qCWarning(lcQuickPlatformFileDialog) << "exec() is not supported for the Qt Quick FileDialog fallback";
}
+/*!
+ \internal
+
+ This is called after QQuickFileDialog::onShow().
+ Both are called in QQuickAbstractDialog::open().
+*/
bool QQuickPlatformFileDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
{
qCDebug(lcQuickPlatformFileDialog) << "show called with flags" << flags <<
@@ -209,6 +226,14 @@ bool QQuickPlatformFileDialog::show(Qt::WindowFlags flags, Qt::WindowModality mo
m_dialog->setRejectLabel(options->isLabelExplicitlySet(QFileDialogOptions::Reject)
? options->labelText(QFileDialogOptions::Reject) : QString());
+ if (options->initiallySelectedFiles().isEmpty()) {
+ if (m_dialog->currentFolder().isEmpty()) {
+ // The user didn't set an initial selectedFile nor currentFolder, so we'll set it to the working directory.
+ qCDebug(lcQuickPlatformFileDialog) << "- calling setCurrentFolder(QDir()) on quick dialog" << parent;
+ m_dialog->setCurrentFolder(QUrl::fromLocalFile(QDir().absolutePath()));
+ }
+ }
+
m_dialog->open();
return true;
}
diff --git a/src/quicktemplates2/qquickdialogbuttonbox.cpp b/src/quicktemplates2/qquickdialogbuttonbox.cpp
index a4197692e4..0c1c1cffac 100644
--- a/src/quicktemplates2/qquickdialogbuttonbox.cpp
+++ b/src/quicktemplates2/qquickdialogbuttonbox.cpp
@@ -497,6 +497,13 @@ QQuickDialogButtonBox::QQuickDialogButtonBox(QQuickItem *parent)
QQuickDialogButtonBox::~QQuickDialogButtonBox()
{
+ Q_D(QQuickDialogButtonBox);
+ // QQuickContainerPrivate does call this, but as our type information has already been
+ // destroyed by that point (since this destructor has already run), it won't call our
+ // implementation. So, we need to make sure our implementation is called. If we don't do this,
+ // the listener we installed on the contentItem won't get removed, possibly resulting in
+ // heap-use-after-frees.
+ contentItemChange(nullptr, d->contentItem);
}
/*!
diff --git a/src/quicktemplates2/qquickitemdelegate.cpp b/src/quicktemplates2/qquickitemdelegate.cpp
index 0b5efcbb11..228b15f0f3 100644
--- a/src/quicktemplates2/qquickitemdelegate.cpp
+++ b/src/quicktemplates2/qquickitemdelegate.cpp
@@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE
//! \instantiates QQuickItemDelegate
\inqmlmodule QtQuick.Controls
\since 5.7
+ \ingroup qtquickcontrols2-delegates
\brief Basic item delegate that can be used in various views and controls.
\image qtquickcontrols2-itemdelegate.gif
diff --git a/src/quicktemplates2/qquicklabel_p_p.h b/src/quicktemplates2/qquicklabel_p_p.h
index a02aa1cf17..12de67d372 100644
--- a/src/quicktemplates2/qquicklabel_p_p.h
+++ b/src/quicktemplates2/qquicklabel_p_p.h
@@ -63,7 +63,7 @@
QT_BEGIN_NAMESPACE
-class QQuickLabelPrivate : public QQuickTextPrivate, public QQuickItemChangeListener
+class Q_AUTOTEST_EXPORT QQuickLabelPrivate : public QQuickTextPrivate, public QQuickItemChangeListener
#if QT_CONFIG(accessibility)
, public QAccessible::ActivationObserver
#endif
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp
index 6b4f3fafce..b1316d7ae1 100644
--- a/src/quicktemplates2/qquickmenu.cpp
+++ b/src/quicktemplates2/qquickmenu.cpp
@@ -240,8 +240,7 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
{
contentData.append(item);
item->setParentItem(contentItem);
- if (qobject_cast<QQuickItemView *>(contentItem))
- QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262
+ QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262
if (complete)
resizeItem(item);
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
diff --git a/src/quicktemplates2/qquickmonthgrid.cpp b/src/quicktemplates2/qquickmonthgrid.cpp
index 52f3c91f3d..f80a2f1824 100644
--- a/src/quicktemplates2/qquickmonthgrid.cpp
+++ b/src/quicktemplates2/qquickmonthgrid.cpp
@@ -70,6 +70,10 @@ QT_BEGIN_NAMESPACE
The visual appearance of MonthGrid can be changed by
implementing a \l {delegate}{custom delegate}.
+ When viewing any given month, MonthGrid shows days from the previous and
+ next month. This means it always shows six rows, even when first or last
+ row is entirely within an adjacent month.
+
\sa DayOfWeekRow, WeekNumberColumn, CalendarModel
*/
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index 4a5e98113d..fa84580777 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -324,7 +324,8 @@ bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy f
const bool onOutside = closePolicy & (flags & outsideFlags);
const bool onOutsideParent = closePolicy & (flags & outsideParentFlags);
- if (onOutside || onOutsideParent) {
+
+ if ((onOutside && outsidePressed) || (onOutsideParent && outsideParentPressed)) {
if (!contains(pos) && (!dimmer || dimmer->contains(dimmer->mapFromScene(pos)))) {
if (!onOutsideParent || !parentItem || !parentItem->contains(parentItem->mapFromScene(pos))) {
closeOrReject();
@@ -368,6 +369,8 @@ bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulo
{
Q_UNUSED(timestamp);
pressPoint = point;
+ outsidePressed = !contains(point);
+ outsideParentPressed = outsidePressed && parentItem && !parentItem->contains(parentItem->mapFromScene(point));
tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
return blockInput(item, point);
}
@@ -384,6 +387,8 @@ bool QQuickPopupPrivate::handleRelease(QQuickItem *item, const QPointF &point, u
if (item != popupItem && !contains(pressPoint))
tryClose(point, QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
pressPoint = QPointF();
+ outsidePressed = false;
+ outsideParentPressed = false;
touchId = -1;
return blockInput(item, point);
}
diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h
index 312caaa1fb..32789d3b7e 100644
--- a/src/quicktemplates2/qquickpopup_p_p.h
+++ b/src/quicktemplates2/qquickpopup_p_p.h
@@ -185,6 +185,8 @@ public:
bool hadActiveFocusBeforeExitTransition = false;
bool interactive = true;
bool hasClosePolicy = false;
+ bool outsidePressed = false;
+ bool outsideParentPressed = false;
int touchId = -1;
qreal x = 0;
qreal y = 0;
diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp
index a1c9ffd431..b9b3091952 100644
--- a/src/quicktemplates2/qquickscrollbar.cpp
+++ b/src/quicktemplates2/qquickscrollbar.cpp
@@ -81,7 +81,7 @@ QT_BEGIN_NAMESPACE
\list
\li \l orientation
\li \l position
- \li \l size
+ \li \l {ScrollBar::} {size}
\li \l active
\endlist
diff --git a/src/quicktemplates2/qquickselectionrectangle.cpp b/src/quicktemplates2/qquickselectionrectangle.cpp
index 69eda47f9b..37e60e0c2c 100644
--- a/src/quicktemplates2/qquickselectionrectangle.cpp
+++ b/src/quicktemplates2/qquickselectionrectangle.cpp
@@ -210,11 +210,12 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
});
QObject::connect(m_dragHandler, &QQuickDragHandler::activeChanged, [this]() {
- const QPointF pos = m_dragHandler->centroid().position();
+ const QPointF startPos = m_dragHandler->centroid().pressPosition();
+ const QPointF dragPos = m_dragHandler->centroid().position();
if (m_dragHandler->active()) {
m_selectable->clearSelection();
- m_selectable->setSelectionStartPos(pos);
- m_selectable->setSelectionEndPos(pos);
+ m_selectable->setSelectionStartPos(startPos);
+ m_selectable->setSelectionEndPos(dragPos);
m_draggedHandle = nullptr;
updateHandles();
updateActiveState(true);
diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp
index d1990a4227..eda21b4532 100644
--- a/src/quicktemplates2/qquickspinbox.cpp
+++ b/src/quicktemplates2/qquickspinbox.cpp
@@ -105,6 +105,9 @@ static const int AUTO_REPEAT_INTERVAL = 100;
This signal is emitted when the spin box value has been interactively
modified by the user by either touch, mouse, wheel, or keys.
+ In the case of interaction via keyboard, the signal is only emitted
+ when the text has been accepted; meaning when the enter or return keys
+ are pressed, or the input field loses focus.
*/
class QQuickSpinBoxPrivate : public QQuickControlPrivate
diff --git a/src/quicktemplates2/qquicktextarea.cpp b/src/quicktemplates2/qquicktextarea.cpp
index 127c8d410c..72794fd36b 100644
--- a/src/quicktemplates2/qquicktextarea.cpp
+++ b/src/quicktemplates2/qquicktextarea.cpp
@@ -461,9 +461,6 @@ void QQuickTextAreaPrivate::readOnlyChanged(bool isReadOnly)
if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
accessibleAttached->set_readOnly(isReadOnly);
#endif
-#if QT_CONFIG(cursor)
- q_func()->setCursor(isReadOnly && !selectByMouse ? Qt::ArrowCursor : Qt::IBeamCursor);
-#endif
}
#if QT_CONFIG(accessibility)
@@ -545,9 +542,7 @@ QQuickTextArea::QQuickTextArea(QQuickItem *parent)
setAcceptedMouseButtons(Qt::AllButtons);
d->setImplicitResizeEnabled(false);
d->pressHandler.control = this;
-#if QT_CONFIG(cursor)
- setCursor(Qt::IBeamCursor);
-#endif
+
QObjectPrivate::connect(this, &QQuickTextEdit::readOnlyChanged,
d, &QQuickTextAreaPrivate::readOnlyChanged);
}
diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h
index 47026c86c0..fd38844446 100644
--- a/src/quicktestutils/quick/viewtestutils_p.h
+++ b/src/quicktestutils/quick/viewtestutils_p.h
@@ -191,7 +191,8 @@ namespace QQuickViewTestUtils
int m_rowCount;
};
- bool testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx);
+ [[nodiscard]] bool testVisibleItems(const QQuickItemViewPrivate *priv,
+ bool *nonUnique, FxViewItem **failItem, int *expectedIdx);
}
namespace QQuickTouchUtils {
@@ -199,8 +200,9 @@ namespace QQuickTouchUtils {
}
namespace QQuickTest {
- bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut = true, QByteArray *errorMessage = nullptr);
- bool showView(QQuickView &v, const QUrl &url);
+ [[nodiscard]] bool initView(QQuickView &v, const QUrl &url,
+ bool moveMouseOut = true, QByteArray *errorMessage = nullptr);
+ [[nodiscard]] bool showView(QQuickView &v, const QUrl &url);
}
QT_END_NAMESPACE
diff --git a/src/quicktestutils/quick/visualtestutils.cpp b/src/quicktestutils/quick/visualtestutils.cpp
index 043b22d763..bd66c2aa60 100644
--- a/src/quicktestutils/quick/visualtestutils.cpp
+++ b/src/quicktestutils/quick/visualtestutils.cpp
@@ -165,7 +165,8 @@ QQuickItem *QQuickVisualTestUtils::findViewDelegateItem(QQuickItemView *itemView
return itemView->itemAtIndex(index);
}
-QQuickVisualTestUtils::QQuickApplicationHelper::QQuickApplicationHelper(QQmlDataTest *testCase, const QString &testFilePath, const QStringList &qmlImportPaths, const QVariantMap &initialProperties)
+QQuickVisualTestUtils::QQuickApplicationHelper::QQuickApplicationHelper(QQmlDataTest *testCase,
+ const QString &testFilePath, const QVariantMap &initialProperties, const QStringList &qmlImportPaths)
{
for (const auto &path : qmlImportPaths)
engine.addImportPath(path);
diff --git a/src/quicktestutils/quick/visualtestutils_p.h b/src/quicktestutils/quick/visualtestutils_p.h
index a4b558d4e6..00824f23cd 100644
--- a/src/quicktestutils/quick/visualtestutils_p.h
+++ b/src/quicktestutils/quick/visualtestutils_p.h
@@ -56,12 +56,10 @@ namespace QQuickVisualTestUtils
void dumpTree(QQuickItem *parent, int depth = 0);
- bool delegateVisible(QQuickItem *item);
-
void moveMouseAway(QQuickWindow *window);
void centerOnScreen(QQuickWindow *window);
- bool delegateVisible(QQuickItem *item);
+ [[nodiscard]] bool delegateVisible(QQuickItem *item);
/*
Find an item with the specified objectName. If index is supplied then the
@@ -77,7 +75,10 @@ namespace QQuickVisualTestUtils
continue;
if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
if (index != -1) {
- QQmlExpression e(qmlContext(item), item, u"index"_qs);
+ QQmlContext *context = qmlContext(item);
+ if (!context->isValid())
+ continue;
+ QQmlExpression e(context, item, u"index"_qs);
if (e.evaluate().toInt() == index)
return static_cast<T*>(item);
} else {
@@ -163,7 +164,7 @@ namespace QQuickVisualTestUtils
afterwards to assign the delegate.
*/
template<typename T>
- bool findViewDelegateItem(QQuickItemView *itemView, int index, T &delegateItem,
+ [[nodiscard]] bool findViewDelegateItem(QQuickItemView *itemView, int index, T &delegateItem,
FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex)
{
delegateItem = qobject_cast<T>(findViewDelegateItem(itemView, index, flags));
@@ -174,8 +175,8 @@ namespace QQuickVisualTestUtils
{
public:
QQuickApplicationHelper(QQmlDataTest *testCase, const QString &testFilePath,
- const QStringList &qmlImportPaths = {},
- const QVariantMap &initialProperties = {});
+ const QVariantMap &initialProperties = {},
+ const QStringList &qmlImportPaths = {});
// Return a C-style string instead of QString because that's what QTest uses for error messages,
// so it saves code at the calling site.
diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
index e1ac1ca22d..c11c537715 100644
--- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
+++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt
@@ -74,6 +74,7 @@ set(qml_files
functionLookup.qml
funcWithParams.qml
functionReturningVoid.qml
+ functionTakingVar.qml
globals.qml
idAccess.qml
immediateQuit.qml
@@ -87,6 +88,7 @@ set(qml_files
interactive.qml
interceptor.qml
isnan.qml
+ javaScriptArgument.qml
jsMathObject.qml
jsimport.qml
jsmoduleimport.qml
diff --git a/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
new file mode 100644
index 0000000000..1765bfcab9
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/functionTakingVar.qml
@@ -0,0 +1,11 @@
+pragma Strict
+import QtQml
+
+QtObject {
+ property var c;
+
+ function a(b: var) {
+ c = b;
+ }
+
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml b/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml
new file mode 100644
index 0000000000..f52325e915
--- /dev/null
+++ b/tests/auto/qml/qmlcppcodegen/data/javaScriptArgument.qml
@@ -0,0 +1,33 @@
+import QtQml
+
+QtObject {
+ function absMinusOne(amount: real) : real {
+ // Access it before the condition below, to make sure we still get the original
+ var minusOne = amount !== 0 ? -1 : 0;
+
+ // The condition causes the original arguemnt to be overwritten rather than a new
+ // register to be allocated
+ if (amount < 0)
+ amount = -amount;
+
+ return amount + minusOne;
+ }
+
+ property real a: absMinusOne(-5)
+ property real b: absMinusOne(10)
+
+ function stringMinusOne(amount: real) : string {
+ // Access it before the condition below, to make sure we still get the original
+ var minusOne = amount !== 0 ? -1 : 0;
+
+ // The condition causes the original arguemnt to be overwritten rather than a new
+ // register to be allocated
+ if (amount < 0)
+ amount = -amount + "t";
+
+ return amount + minusOne;
+ }
+
+ property string c: stringMinusOne(-5)
+ property string d: stringMinusOne(10)
+}
diff --git a/tests/auto/qml/qmlcppcodegen/data/listIndices.qml b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
index b5fda4ef0d..9df172b2e6 100644
--- a/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/listIndices.qml
@@ -5,10 +5,18 @@ QtObject {
id: self
property list<QtObject> items
property int numItems: items.length
+ property QtObject fractional: items[2.25]
+ property QtObject negativeZero: items[-1 * 0]
+ property QtObject infinity: items[1 / 0]
+ property QtObject nan: items[1 - "a"]
Component.onCompleted: {
items.length = 3
for (var i = 0; i < 3; ++i)
items[i] = self
+
+ items[2.25] = null
+ items[1 / 0] = self
+ items[1 - "a"] = self
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index bd6fb37677..3d5d5ae37d 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -129,6 +129,8 @@ private slots:
void evadingAmbiguity();
void fromBoolValue();
void invisibleTypes();
+ void functionTakingVar();
+ void javaScriptArgument();
};
void tst_QmlCppCodegen::simpleBinding()
@@ -1553,6 +1555,10 @@ void tst_QmlCppCodegen::listIndices()
for (int i = 0; i < 3; ++i)
QCOMPARE(list.at(i), o.data());
QCOMPARE(o->property("numItems").toInt(), 3);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("fractional")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("negativeZero")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("infinity")), nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("nan")), nullptr);
}
void tst_QmlCppCodegen::jsMathObject()
@@ -1977,6 +1983,41 @@ void tst_QmlCppCodegen::invisibleTypes()
// QCOMPARE(meta->className(), "DerivedFromInvisible");
}
+void tst_QmlCppCodegen::functionTakingVar()
+{
+ QQmlEngine engine;
+ const QUrl document(u"qrc:/TestTypes/functionTakingVar.qml"_qs);
+ QQmlComponent c(&engine, document);
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ QVERIFY(!o->property("c").isValid());
+
+ int value = 11;
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(&engine);
+ void *args[] = { nullptr, reinterpret_cast<void *>(std::addressof(value)) };
+ QMetaType types[] = { QMetaType::fromType<void>(), QMetaType::fromType<std::decay_t<int>>() };
+ e->executeRuntimeFunction(document, 0, o.data(), 1, args, types);
+
+ QCOMPARE(o->property("c"), QVariant::fromValue<int>(11));
+}
+
+void tst_QmlCppCodegen::javaScriptArgument()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/javaScriptArgument.qml"_qs));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toDouble(), 4.0);
+ QCOMPARE(o->property("b").toDouble(), 9.0);
+ QCOMPARE(o->property("c").toString(), u"5t-1"_qs);
+ QCOMPARE(o->property("d").toString(), u"9"_qs);
+}
+
void tst_QmlCppCodegen::runInterpreted()
{
#ifdef Q_OS_ANDROID
diff --git a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
index 5f6f8e83b2..d1ad5858a5 100644
--- a/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
+++ b/tests/auto/qml/qmltyperegistrar/CMakeLists.txt
@@ -74,3 +74,5 @@ qt_add_qml_module(tst-qmltyperegistrar-with-dashes
foo.cpp foo.h
)
qt_autogen_tools_initial_setup(tst-qmltyperegistrar-with-dashesplugin)
+
+add_subdirectory(VersionZero)
diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt b/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt
new file mode 100644
index 0000000000..39dfccebd1
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/VersionZero/CMakeLists.txt
@@ -0,0 +1,20 @@
+qt_add_library(tst_qmltyperegistrar_major_version_zero)
+qt_autogen_tools_initial_setup(tst_qmltyperegistrar_major_version_zero)
+target_link_libraries(tst_qmltyperegistrar_major_version_zero PRIVATE Qt::Core Qt::Qml)
+qt_enable_autogen_tool(tst_qmltyperegistrar_major_version_zero "moc" ON)
+qt_add_qml_module(tst_qmltyperegistrar_major_version_zero
+ URI VersionZero
+ VERSION 0.1
+ SOURCES
+ version_zero_type.h
+)
+qt_autogen_tools_initial_setup(tst_qmltyperegistrar_major_version_zeroplugin)
+
+# Make sure the backing library is found on Windows next to the executable
+set_target_properties(
+ tst_qmltyperegistrar_major_version_zero
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
+)
diff --git a/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
new file mode 100644
index 0000000000..a3277e6ab3
--- /dev/null
+++ b/tests/auto/qml/qmltyperegistrar/VersionZero/version_zero_type.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VERSION_ZERO_TYPE_H
+#define VERSION_ZERO_TYPE_H
+
+#include <QtQml/qqml.h>
+
+class TypeInModuleMajorVersionZero : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+public:
+ TypeInModuleMajorVersionZero(QObject *parent = nullptr) : QObject(parent) {}
+};
+
+#endif // VERSION_ZERO_TYPE_H
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
index 3fc746552e..68a21d8db8 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp
@@ -396,4 +396,14 @@ void tst_qmltyperegistrar::foreignRevisionedProperty()
}
#endif
+void tst_qmltyperegistrar::typeInModuleMajorVersionZero()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData(QStringLiteral("import VersionZero\n"
+ "TypeInModuleMajorVersionZero {}\n").toUtf8(),
+ QUrl(QTest::currentDataTag()));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+}
+
QTEST_MAIN(tst_qmltyperegistrar)
diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
index 385d3b6666..f2ccaba618 100644
--- a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
+++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h
@@ -532,6 +532,7 @@ private slots:
void addRemoveVersion_data();
void addRemoveVersion();
+ void typeInModuleMajorVersionZero();
private:
QByteArray qmltypesData;
diff --git a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
index d9fbf6517d..caf1e2edaa 100644
--- a/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlapplicationengine/CMakeLists.txt
@@ -55,3 +55,4 @@ qt_internal_extend_target(tst_qqmlapplicationengine CONDITION NOT ANDROID AND NO
QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
)
add_subdirectory(testapp)
+add_subdirectory(androidassets)
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
new file mode 100644
index 0000000000..1c0d305311
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/CMakeLists.txt
@@ -0,0 +1,18 @@
+qt_internal_add_test(tst_androidassets
+ SOURCES
+ tst_androidassets.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+# add qml/*.qml files as assets instead of resources
+
+file(
+ COPY qml/main.qml
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/android-build/assets/qml/")
+
+file(
+ COPY qml/pages/MainPage.qml
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/android-build/assets/qml/pages/")
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml
new file mode 100644
index 0000000000..6901572b00
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/main.qml
@@ -0,0 +1,13 @@
+import QtQuick
+
+// relative import: has to work when loading from android assets by path or by URL
+import "pages"
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+
+ MainPage { }
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml
new file mode 100644
index 0000000000..c9549a928a
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/qml/pages/MainPage.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Rectangle {
+ anchors.fill: parent
+ color: "#ddd"
+
+ Text {
+ anchors.centerIn: parent
+ text: "Qt 6"
+ }
+}
diff --git a/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
new file mode 100644
index 0000000000..0751e23f45
--- /dev/null
+++ b/tests/auto/qml/qqmlapplicationengine/androidassets/tst_androidassets.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtTest/qsignalspy.h>
+#include <QtTest/qtest.h>
+
+class tst_AndroidAssets : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void loadsFromAssetsPath();
+ void loadsFromAssetsUrl();
+
+private:
+
+ static QString pathPrefix()
+ {
+#ifdef Q_OS_ANDROID
+ return QStringLiteral("assets:");
+#else
+ // Even when not running on android we can check that the copying to build dir worked.
+ return QCoreApplication::applicationDirPath() + QStringLiteral("/android-build/assets");
+#endif
+ }
+
+ static QString urlPrefix() {
+#ifdef Q_OS_ANDROID
+ return pathPrefix();
+#else
+ return QStringLiteral("file:") + pathPrefix();
+#endif
+ }
+};
+
+
+void tst_AndroidAssets::loadsFromAssetsPath()
+{
+ QQmlApplicationEngine engine;
+
+ // load QML file from assets, by path:
+ engine.load(pathPrefix() + QStringLiteral("/qml/main.qml"));
+ QTRY_VERIFY(engine.rootObjects().length() == 1);
+}
+
+void tst_AndroidAssets::loadsFromAssetsUrl()
+{
+ QQmlApplicationEngine engine;
+
+ // load QML file from assets, by URL:
+ engine.load(QUrl(urlPrefix() + QStringLiteral("/qml/main.qml")));
+ QTRY_VERIFY(engine.rootObjects().length() == 1);
+}
+
+QTEST_MAIN(tst_AndroidAssets)
+
+#include "tst_androidassets.moc"
diff --git a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
index a1c8daddcf..77909d8576 100644
--- a/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
+++ b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp
@@ -38,19 +38,145 @@ public:
tst_qqmlfile() {}
private Q_SLOTS:
+ void isLocalFile_data();
+ void isLocalFile();
+
+ void urlToLocalFileOrQrcOverloads_data();
void urlToLocalFileOrQrcOverloads();
+
+private:
+ void urlData();
};
+void tst_qqmlfile::urlData()
+{
+ QTest::addColumn<QString>("urlString");
+ QTest::addColumn<bool>("isLocal");
+ QTest::addColumn<QString>("localPath");
+
+ const QString invalid;
+ const QString relative = QStringLiteral("foo/bar");
+ const QString absolute = QStringLiteral("/foo/bar");
+
+ QTest::addRow("plain empty") << QStringLiteral("") << false << invalid;
+ QTest::addRow("plain no slash") << QStringLiteral("foo/bar") << false << invalid;
+ QTest::addRow("plain 1 slash") << QStringLiteral("/foo/bar") << false << invalid;
+ QTest::addRow("plain 2 slashes") << QStringLiteral("//foo/bar") << false << invalid;
+ QTest::addRow("plain 3 slashes") << QStringLiteral("///foo/bar") << false << invalid;
+
+ QTest::addRow(": empty") << QStringLiteral(":") << false << invalid;
+ QTest::addRow(": no slash") << QStringLiteral(":foo/bar") << false << invalid;
+ QTest::addRow(": 1 slash") << QStringLiteral(":/foo/bar") << false << invalid;
+ QTest::addRow(": 2 slashes") << QStringLiteral("://foo/bar") << false << invalid;
+ QTest::addRow(": 3 slashes") << QStringLiteral(":///foo/bar") << false << invalid;
+
+ QTest::addRow("C empty") << QStringLiteral("C:") << false << invalid;
+ QTest::addRow("C no slash") << QStringLiteral("C:foo/bar") << false << invalid;
+ QTest::addRow("C 1 slash") << QStringLiteral("C:/foo/bar") << false << invalid;
+ QTest::addRow("C 2 slashes") << QStringLiteral("C://foo/bar") << false << invalid;
+ QTest::addRow("C 3 slashes") << QStringLiteral("C:///foo/bar") << false << invalid;
+
+ QTest::addRow("file empty") << QStringLiteral("file:") << true << QString();
+ QTest::addRow("file no slash") << QStringLiteral("file:foo/bar") << true << relative;
+ QTest::addRow("file 1 slash") << QStringLiteral("file:/foo/bar") << true << absolute;
+ QTest::addRow("file 2 slashes") << QStringLiteral("file://foo/bar") << true << QStringLiteral("//foo/bar");
+ QTest::addRow("file 3 slashes") << QStringLiteral("file:///foo/bar") << true << absolute;
+
+ QTest::addRow("qrc empty") << QStringLiteral("qrc:") << true << QStringLiteral(":");
+ QTest::addRow("qrc no slash") << QStringLiteral("qrc:foo/bar") << true << u':' + relative;
+ QTest::addRow("qrc 1 slash") << QStringLiteral("qrc:/foo/bar") << true << u':' + absolute;
+ QTest::addRow("qrc 2 slashes") << QStringLiteral("qrc://foo/bar") << false << invalid;
+ QTest::addRow("qrc 3 slashes") << QStringLiteral("qrc:///foo/bar") << true << u':' + absolute;
+
+ QTest::addRow("file+stuff empty") << QStringLiteral("file+stuff:") << false << invalid;
+ QTest::addRow("file+stuff no slash") << QStringLiteral("file+stuff:foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 1 slash") << QStringLiteral("file+stuff:/foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 2 slashes") << QStringLiteral("file+stuff://foo/bar") << false << invalid;
+ QTest::addRow("file+stuff 3 slashes") << QStringLiteral("file+stuff:///foo/bar") << false << invalid;
+
+ // "assets:" and "content:" URLs are only treated as local files on android. In contrast to
+ // "qrc:" and "file:" we're not trying to be clever about multiple slashes. Two slashes are
+ // prohibited as that says part of what we would recognize as path is actually a URL authority.
+ // Everything else is android's problem.
+
+#ifdef Q_OS_ANDROID
+ const bool hasAssetsAndContent = true;
+#else
+ const bool hasAssetsAndContent = false;
+#endif
+
+ const QString assetsEmpty = hasAssetsAndContent ? QStringLiteral("assets:") : invalid;
+ const QString assetsRelative = hasAssetsAndContent ? (QStringLiteral("assets:") + relative) : invalid;
+ const QString assetsAbsolute = hasAssetsAndContent ? (QStringLiteral("assets:") + absolute) : invalid;
+ const QString assetsThreeSlashes = hasAssetsAndContent ? (QStringLiteral("assets://") + absolute) : invalid;
+
+ QTest::addRow("assets empty") << QStringLiteral("assets:") << hasAssetsAndContent << assetsEmpty;
+ QTest::addRow("assets no slash") << QStringLiteral("assets:foo/bar") << hasAssetsAndContent << assetsRelative;
+ QTest::addRow("assets 1 slash") << QStringLiteral("assets:/foo/bar") << hasAssetsAndContent << assetsAbsolute;
+ QTest::addRow("assets 2 slashes") << QStringLiteral("assets://foo/bar") << false << invalid;
+ QTest::addRow("assets 3 slashes") << QStringLiteral("assets:///foo/bar") << hasAssetsAndContent << assetsThreeSlashes;
+
+ const QString contentEmpty = hasAssetsAndContent ? QStringLiteral("content:") : invalid;
+ const QString contentRelative = hasAssetsAndContent ? (QStringLiteral("content:") + relative) : invalid;
+ const QString contentAbsolute = hasAssetsAndContent ? (QStringLiteral("content:") + absolute) : invalid;
+ const QString contentThreeSlashes = hasAssetsAndContent ? (QStringLiteral("content://") + absolute) : invalid;
+
+ QTest::addRow("content empty") << QStringLiteral("content:") << hasAssetsAndContent << contentEmpty;
+ QTest::addRow("content no slash") << QStringLiteral("content:foo/bar") << hasAssetsAndContent << contentRelative;
+ QTest::addRow("content 1 slash") << QStringLiteral("content:/foo/bar") << hasAssetsAndContent << contentAbsolute;
+ QTest::addRow("content 2 slashes") << QStringLiteral("content://foo/bar") << false << invalid;
+ QTest::addRow("content 3 slashes") << QStringLiteral("content:///foo/bar") << hasAssetsAndContent << contentThreeSlashes;
+
+
+ // These are local files everywhere. Their paths are only meaningful on android, though.
+ // The inner slashes of the path do not influence the URL parsing.
+
+ QTest::addRow("file:assets empty") << QStringLiteral("file:assets:") << true << QStringLiteral("assets:");
+ QTest::addRow("file:assets no slash") << QStringLiteral("file:assets:foo/bar") << true << QStringLiteral("assets:foo/bar");
+ QTest::addRow("file:assets 1 slash") << QStringLiteral("file:assets:/foo/bar") << true << QStringLiteral("assets:/foo/bar");
+ QTest::addRow("file:assets 2 slashes") << QStringLiteral("file:assets://foo/bar") << true << QStringLiteral("assets://foo/bar");
+ QTest::addRow("file:assets 3 slashes") << QStringLiteral("file:assets:///foo/bar") << true << QStringLiteral("assets:///foo/bar");
+
+ QTest::addRow("file:content empty") << QStringLiteral("file:content:") << true << QStringLiteral("content:");
+ QTest::addRow("file:content no slash") << QStringLiteral("file:content:foo/bar") << true << QStringLiteral("content:foo/bar");
+ QTest::addRow("file:content 1 slash") << QStringLiteral("file:content:/foo/bar") << true << QStringLiteral("content:/foo/bar");
+ QTest::addRow("file:content 2 slashes") << QStringLiteral("file:content://foo/bar") << true << QStringLiteral("content://foo/bar");
+ QTest::addRow("file:content 3 slashes") << QStringLiteral("file:content:///foo/bar") << true << QStringLiteral("content:///foo/bar");
+}
+
+void tst_qqmlfile::isLocalFile_data()
+{
+ urlData();
+}
+
+void tst_qqmlfile::isLocalFile()
+{
+ QFETCH(QString, urlString);
+ QFETCH(bool, isLocal);
+
+ const QUrl url(urlString);
+
+ QCOMPARE(QQmlFile::isLocalFile(urlString), isLocal);
+ QCOMPARE(QQmlFile::isLocalFile(url), isLocal);
+}
+
+void tst_qqmlfile::urlToLocalFileOrQrcOverloads_data()
+{
+ urlData();
+}
+
void tst_qqmlfile::urlToLocalFileOrQrcOverloads()
{
- const QString urlString = QStringLiteral("qrc:///example.qml");
+ QFETCH(QString, urlString);
+ QFETCH(QString, localPath);
+
const QUrl url(urlString);
const QString pathForUrlString = QQmlFile::urlToLocalFileOrQrc(urlString);
const QString pathForUrl = QQmlFile::urlToLocalFileOrQrc(url);
- QCOMPARE(pathForUrlString, pathForUrl);
- QCOMPARE(pathForUrlString, QStringLiteral(":/example.qml"));
+ QCOMPARE(pathForUrlString, localPath);
+ QCOMPARE(pathForUrl, localPath);
}
QTEST_GUILESS_MAIN(tst_qqmlfile)
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index b25dec67f1..ab23f71f3e 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -4418,9 +4418,6 @@ void tst_qqmllanguage::deepProperty()
void tst_qqmllanguage::groupAssignmentFailure()
{
auto ep = std::make_unique<QQmlEngine>();
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component destroyed while completion pending");
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, "This may have been caused by one of the following errors:");
- QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*Cannot set properties on b as it is null.*"));
QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*Invalid property assignment: url expected - Assigning null to incompatible properties in QML is deprecated. This will become a compile error in future versions of Qt..*"));
QQmlComponent component(ep.get(), testFileUrl("groupFailure.qml"));
QScopedPointer<QObject> o(component.create());
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index 25eaa22b86..6310ef7d54 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -361,7 +361,8 @@ void tst_qqmlnotifier::deleteFromHandler()
process.setProgram(QCoreApplication::applicationFilePath());
process.setArguments({"deleteFromHandler"});
process.start();
- QTRY_COMPARE(process.exitStatus(), QProcess::CrashExit);
+ const bool ok = process.waitForFinished(90000);
+ QVERIFY(ok);
const QByteArray output = process.readAllStandardOutput();
QVERIFY(output.contains("QFATAL"));
QVERIFY(output.contains("destroyed while one of its QML signal handlers is in progress"));
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 7e1dc2f3e5..14af747024 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -572,10 +572,28 @@ class TestClassWithClassInfo : public QObject
#define ARRAY_SIZE(arr) \
int(sizeof(arr) / sizeof(arr[0]))
+template <typename T, typename = void>
+struct SizeofOffsetsAndSizes_helper
+{
+ static constexpr size_t value = sizeof(T::offsetsAndSize); // old moc
+};
+
+template <typename T>
+struct SizeofOffsetsAndSizes_helper<T, std::void_t<decltype(T::offsetsAndSizes)>>
+{
+ static constexpr size_t value = sizeof(T::offsetsAndSizes); // new moc
+};
+
+template <typename T>
+constexpr size_t sizeofOffsetsAndSizes(const T &)
+{
+ return SizeofOffsetsAndSizes_helper<T>::value;
+}
+
#define TEST_CLASS(Class) \
QTest::newRow(#Class) \
<< &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) \
- << int(sizeof(qt_meta_stringdata_##Class.offsetsAndSize) / (sizeof(uint) * 2))
+ << int(sizeofOffsetsAndSizes(qt_meta_stringdata_##Class) / (sizeof(uint) * 2))
Q_DECLARE_METATYPE(const QMetaObject*);
diff --git a/tests/auto/qmlls/qmlls/tst_qmlls.cpp b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
index 2d0d38f352..a7276fcedf 100644
--- a/tests/auto/qmlls/qmlls/tst_qmlls.cpp
+++ b/tests/auto/qmlls/qmlls/tst_qmlls.cpp
@@ -108,7 +108,8 @@ tst_Qmlls::tst_Qmlls()
connect(&m_server, &QProcess::readyReadStandardError, this,
[this]() { qWarning() << "LSPerr" << m_server.readAllStandardError(); });
- m_qmllsPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlls");
+ m_qmllsPath =
+ QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + QLatin1String("/qmlls");
#ifdef Q_OS_WIN
m_qmllsPath += QLatin1String(".exe");
#endif
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml
new file mode 100644
index 0000000000..7adb205a1f
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.12
+
+Rectangle {
+ width: 200; height: 200
+
+ DragHandler { }
+
+ Rectangle {
+ objectName: "button"
+ width: 100; height: 40; x: 10; y: 10
+ border.color: "orange"
+ color: ma.pressed ? "lightsteelblue" : "beige"
+
+ MouseArea {
+ id: ma
+ anchors.fill: parent
+ onDoubleClicked: console.log("__")
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
index ffc0cc333d..ccc1364705 100644
--- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp
@@ -55,6 +55,7 @@ private slots:
void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data();
void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch();
void hoverHandlerDoesntHoverOnPress();
+ void doubleClickInMouseAreaWithDragHandlerInGrandparent();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -209,6 +210,24 @@ void tst_MouseAreaInterop::hoverHandlerDoesntHoverOnPress() // QTBUG-72843
QCOMPARE(hoveredChangedSpy.count(), 0);
}
+void tst_MouseAreaInterop::doubleClickInMouseAreaWithDragHandlerInGrandparent()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("dragHandlerInMouseAreaGrandparent.qml")));
+
+ QQuickDragHandler *handler = window.rootObject()->findChild<QQuickDragHandler*>();
+ QVERIFY(handler);
+ QSignalSpy dragActiveSpy(handler, &QQuickDragHandler::activeChanged);
+ QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>();
+ QVERIFY(ma);
+ QSignalSpy dClickSpy(ma, &QQuickMouseArea::doubleClicked);
+ QPoint p = ma->mapToScene(ma->boundingRect().center()).toPoint();
+
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(dClickSpy.count(), 1);
+ QCOMPARE(dragActiveSpy.count(), 0);
+}
+
QTEST_MAIN(tst_MouseAreaInterop)
#include "tst_mousearea_interop.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
index d22b3d86aa..f9f3216fbe 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp
@@ -149,6 +149,8 @@ void tst_PointHandler::tabletStylus()
QQuickPointHandler *handler = window->rootObject()->findChild<QQuickPointHandler *>("pointHandler");
QVERIFY(handler);
handler->setAcceptedDevices(QInputDevice::DeviceType::Stylus);
+ // avoid generating a double click
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
QSignalSpy activeSpy(handler, SIGNAL(activeChanged()));
QSignalSpy pointSpy(handler, SIGNAL(pointChanged()));
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
index 8d2e36d921..8d2e36d921 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index fb817ca045..c17fb5777a 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -75,6 +75,8 @@ private slots:
void rightLongPressIgnoreWheel();
void negativeZStackingOrder();
void nonTopLevelParentWindow();
+ void nestedDoubleTap_data();
+ void nestedDoubleTap();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName,
@@ -837,7 +839,7 @@ void tst_TapHandler::rightLongPressIgnoreWheel()
void tst_TapHandler::negativeZStackingOrder() // QTBUG-83114
{
QScopedPointer<QQuickView> windowPtr;
- createView(windowPtr, "tapHandlersOverlapped.qml");
+ createView(windowPtr, "nested.qml");
QQuickView *window = windowPtr.data();
QQuickItem *root = window->rootObject();
@@ -891,6 +893,42 @@ void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716
QCOMPARE(root->property("tapCount").toInt(), 2);
}
+void tst_TapHandler::nestedDoubleTap_data()
+{
+ QTest::addColumn<QQuickTapHandler::GesturePolicy>("childGesturePolicy");
+
+ QTest::newRow("DragThreshold") << QQuickTapHandler::GesturePolicy::DragThreshold;
+ QTest::newRow("WithinBounds") << QQuickTapHandler::GesturePolicy::WithinBounds;
+ QTest::newRow("ReleaseWithinBounds") << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds;
+ QTest::newRow("DragWithinBounds") << QQuickTapHandler::GesturePolicy::DragWithinBounds;
+}
+
+void tst_TapHandler::nestedDoubleTap() // QTBUG-102625
+{
+ QFETCH(QQuickTapHandler::GesturePolicy, childGesturePolicy);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("nested.qml")));
+ QQuickItem *root = window.rootObject();
+ QQuickTapHandler *parentTapHandler = root->findChild<QQuickTapHandler*>("parentTapHandler");
+ QVERIFY(parentTapHandler);
+ QSignalSpy parentSpy(parentTapHandler, &QQuickTapHandler::doubleTapped);
+ QQuickTapHandler *childTapHandler = root->findChild<QQuickTapHandler*>("childTapHandler");
+ QVERIFY(childTapHandler);
+ QSignalSpy childSpy(childTapHandler, &QQuickTapHandler::doubleTapped);
+ childTapHandler->setGesturePolicy(childGesturePolicy);
+
+ QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100));
+
+ QCOMPARE(childSpy.count(), 1);
+ // If the child gets by with a passive grab, both handlers see tap and double-tap.
+ // If the child takes an exclusive grab and stops event propagation, the parent doesn't see them.
+ QCOMPARE(parentSpy.count(),
+ childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 1 : 0);
+ QCOMPARE(root->property("taps").toList().count(),
+ childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2);
+}
+
QTEST_MAIN(tst_TapHandler)
#include "tst_qquicktaphandler.moc"
diff --git a/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml b/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml
new file mode 100644
index 0000000000..b36df04d45
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/nestedmouseareapce.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Flickable {
+ id: outer
+ objectName: "flickable"
+ width: 400
+ height: 400
+ contentX: 50
+ contentY: 50
+ contentWidth: 500
+ contentHeight: 500
+
+ Rectangle {
+ objectName: "nested"
+ x: 100
+ y: 100
+ width: 300
+ height: 300
+
+ color: "yellow"
+ MouseArea {
+ anchors.fill: parent
+ propagateComposedEvents: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 9682079e84..8180d69602 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -217,6 +217,7 @@ private slots:
void stopAtBounds();
void stopAtBounds_data();
void nestedMouseAreaUsingTouch();
+ void nestedMouseAreaPropagateComposedEvents();
void nestedSliderUsingTouch();
void nestedSliderUsingTouch_data();
void pressDelayWithLoader();
@@ -2123,6 +2124,27 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch()
QVERIFY(nested->y() < 100.0);
}
+void tst_qquickflickable::nestedMouseAreaPropagateComposedEvents()
+{
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("nestedmouseareapce.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtils::centerOnScreen(window.data());
+ QQuickViewTestUtils::moveMouseAway(window.data());
+ window->show();
+ QVERIFY(window->rootObject() != nullptr);
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable != nullptr);
+
+ QCOMPARE(flickable->contentY(), 50.0f);
+ flickWithTouch(window.data(), QPoint(100, 300), QPoint(100, 200));
+
+ // flickable should have moved
+ QVERIFY(!qFuzzyCompare(flickable->contentY(), 50.0));
+}
+
void tst_qquickflickable::nestedSliderUsingTouch_data()
{
QTest::addColumn<bool>("keepMouseGrab");
@@ -2815,7 +2837,7 @@ void tst_qquickflickable::flickWhenRotated_data()
for (const auto fr : rotations) {
if (pr <= 90) {
for (const auto s : scales)
- QTest::addRow("parent: %g, flickable: %g, scale: %g", pr, fr) << pr << fr << s;
+ QTest::addRow("parent: %g, flickable: %g, scale: %g", pr, fr, s) << pr << fr << s;
} else {
// don't bother trying every scale with every set of rotations, to save time
QTest::addRow("parent: %g, flickable: %g, scale: 1", pr, fr) << pr << fr << qreal(1);
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index 3c5d5ee25b..9b30dd2bc0 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -4,12 +4,6 @@ opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1
-#QTBUG-75960
-#QTBUG-76652
-[currentIndex]
-opensuse-leap
-ubuntu-18.04
-ubuntu-20.04
# QTBUG-75202
[contentHeightWithDelayRemove]
macos ci
diff --git a/tests/auto/quick/qquicklistview/data/headerCrash.qml b/tests/auto/quick/qquicklistview/data/headerCrash.qml
index 124fa894f2..972ecb2906 100644
--- a/tests/auto/quick/qquicklistview/data/headerCrash.qml
+++ b/tests/auto/quick/qquicklistview/data/headerCrash.qml
@@ -12,7 +12,7 @@ ListView {
}
delegate: Rectangle {
- width: parent.width; height: 20
+ width: myList.width; height: 20
color: index % 2 ? "green" : "red"
}
diff --git a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
index e0acaf49e4..ef959b10b4 100644
--- a/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
+++ b/tests/auto/quick/qquicklistview/data/outsideViewportChangeNotAffectingView.qml
@@ -72,7 +72,7 @@ Item {
ListElement { size: 300; }
}
delegate: Rectangle {
- width: parent.width
+ width: list.width
color: index % 2 == 0 ? "red" : "blue"
height: size
Text { anchors.centerIn: parent; text: index }
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 6975fa2dbd..68d8c5c6e2 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -434,6 +434,9 @@ void tst_QQuickListView::cleanupTestCase()
template <class T>
void tst_QQuickListView::items(const QUrl &source)
{
+ // Make sure we outlive the view, or the context property will become null.
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -444,7 +447,6 @@ void tst_QQuickListView::items(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -515,6 +517,8 @@ void tst_QQuickListView::items(const QUrl &source)
template <class T>
void tst_QQuickListView::changed(const QUrl &source)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -525,7 +529,6 @@ void tst_QQuickListView::changed(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -553,6 +556,8 @@ void tst_QQuickListView::changed(const QUrl &source)
template <class T>
void tst_QQuickListView::inserted(const QUrl &source)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
@@ -565,7 +570,6 @@ void tst_QQuickListView::inserted(const QUrl &source)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -657,11 +661,12 @@ void tst_QQuickListView::inserted_more(QQuickItemView::VerticalLayoutDirection v
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -958,6 +963,8 @@ void tst_QQuickListView::insertBeforeVisible_data()
template <class T>
void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -967,7 +974,6 @@ void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -1321,6 +1327,8 @@ void tst_QQuickListView::removed_more_data()
template <class T>
void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayoutDirection verticalLayoutDirection)
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
T model;
@@ -1330,7 +1338,6 @@ void tst_QQuickListView::clear(const QUrl &source, QQuickItemView::VerticalLayou
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(source);
@@ -1853,6 +1860,8 @@ void tst_QQuickListView::multipleChanges_data()
void tst_QQuickListView::swapWithFirstItem()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -1862,7 +1871,6 @@ void tst_QQuickListView::swapWithFirstItem()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -1881,6 +1889,8 @@ void tst_QQuickListView::swapWithFirstItem()
void tst_QQuickListView::checkCountForMultiColumnModels()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
// Check that a list view will only load items for the first
// column, even if the model reports that it got several columns.
// We test this since QQmlDelegateModel has been changed to
@@ -1898,7 +1908,6 @@ void tst_QQuickListView::checkCountForMultiColumnModels()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -2035,6 +2044,8 @@ void tst_QQuickListView::enforceRange_withoutHighlight()
void tst_QQuickListView::spacing()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -2044,7 +2055,6 @@ void tst_QQuickListView::spacing()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -2965,8 +2975,8 @@ void tst_QQuickListView::keyNavigation()
for (int i = 0; i < 30; i++)
model.addItem("Item" + QString::number(i), "");
- QQuickView *window = getView();
QScopedPointer<TestObject> testObject(new TestObject);
+ QQuickView *window = getView();
window->rootContext()->setContextProperty("testModel", &model);
window->rootContext()->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3170,6 +3180,8 @@ void tst_QQuickListView::itemListFlicker()
void tst_QQuickListView::cacheBuffer()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -3179,7 +3191,6 @@ void tst_QQuickListView::cacheBuffer()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3272,6 +3283,8 @@ void tst_QQuickListView::cacheBuffer()
void tst_QQuickListView::positionViewAtBeginningEnd()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -3281,7 +3294,6 @@ void tst_QQuickListView::positionViewAtBeginningEnd()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->show();
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3330,6 +3342,8 @@ void tst_QQuickListView::positionViewAtIndex()
QFETCH(QQuickListView::PositionMode, mode);
QFETCH(qreal, contentY);
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QaimModel model;
@@ -3339,7 +3353,6 @@ void tst_QQuickListView::positionViewAtIndex()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->show();
window->setSource(testFileUrl("listviewtest.qml"));
@@ -3679,6 +3692,8 @@ void tst_QQuickListView::manualHighlight()
void tst_QQuickListView::QTBUG_11105()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
for (int i = 0; i < 30; i++)
@@ -3687,7 +3702,6 @@ void tst_QQuickListView::QTBUG_11105()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4349,6 +4363,8 @@ void tst_QQuickListView::resetModel_headerFooter()
void tst_QQuickListView::resizeView()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
for (int i = 0; i < 40; i++)
@@ -4357,7 +4373,6 @@ void tst_QQuickListView::resizeView()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4458,6 +4473,8 @@ void tst_QQuickListView::resizeViewAndRepaint()
void tst_QQuickListView::sizeLessThan1()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
QaimModel model;
@@ -4467,7 +4484,6 @@ void tst_QQuickListView::sizeLessThan1()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("sizelessthan1.qml"));
@@ -4588,6 +4604,8 @@ void tst_QQuickListView::resizeFirstDelegate()
{
// QTBUG-20712: Content Y jumps constantly if first delegate height == 0
// and other delegates have height > 0
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QScopedPointer<QQuickView> window(createView());
// bug only occurs when all items in the model are visible
@@ -4598,7 +4616,6 @@ void tst_QQuickListView::resizeFirstDelegate()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -4796,6 +4813,8 @@ void tst_QQuickListView::indexAt_itemAt()
QFETCH(qreal, y);
QFETCH(int, index);
+ QScopedPointer<TestObject> testObject(new TestObject);
+
QQuickView *window = getView();
QaimModel model;
@@ -4805,7 +4824,6 @@ void tst_QQuickListView::indexAt_itemAt()
QQmlContext *ctxt = window->rootContext();
ctxt->setContextProperty("testModel", &model);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("listviewtest.qml"));
@@ -6673,8 +6691,8 @@ void tst_QQuickListView::populateTransitions()
model.addItem("item" + QString::number(i), "");
}
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
- QScopedPointer<TestObject> testObject(new TestObject(window->rootContext()));
window->rootContext()->setContextProperty("testModel", &model);
window->rootContext()->setContextProperty("testObject", testObject.data());
window->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
@@ -6797,12 +6815,12 @@ void tst_QQuickListView::populateTransitions_data()
void tst_QQuickListView::sizeTransitions()
{
QFETCH(bool, topToBottom);
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
QaimModel model;
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("topToBottom", topToBottom);
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testObject", &model);
window->setSource(testFileUrl("sizeTransitions.qml"));
window->show();
@@ -6857,9 +6875,9 @@ void tst_QQuickListView::addTransitions()
QaimModel model_targetItems_transitionFrom;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7052,9 +7070,9 @@ void tst_QQuickListView::moveTransitions()
QaimModel model_targetItems_transitionVia;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7254,9 +7272,9 @@ void tst_QQuickListView::removeTransitions()
QaimModel model_targetItems_transitionTo;
QaimModel model_displacedItems_transitionVia;
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
@@ -7452,9 +7470,9 @@ void tst_QQuickListView::displacedTransitions()
QPointF moveDisplaced_transitionVia(50, -100);
QPointF removeDisplaced_transitionVia(150, 100);
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject(window));
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
@@ -7678,9 +7696,9 @@ void tst_QQuickListView::multipleTransitions()
for (int i = 0; i < initialCount; i++)
model.addItem("Original item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject);
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject);
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
@@ -7845,9 +7863,9 @@ void tst_QQuickListView::multipleDisplaced()
for (int i = 0; i < 30; i++)
model.addItem("Original item" + QString::number(i), "");
+ QScopedPointer<TestObject> testObject(new TestObject());
QQuickView *window = getView();
QQmlContext *ctxt = window->rootContext();
- QScopedPointer<TestObject> testObject(new TestObject(window));
ctxt->setContextProperty("testModel", &model);
ctxt->setContextProperty("testObject", testObject.data());
window->setSource(testFileUrl("multipleDisplaced.qml"));
@@ -9518,8 +9536,8 @@ void tst_QQuickListView::itemFiltered()
QScopedPointer<QQuickView> window(createView());
window->engine()->rootContext()->setContextProperty("_model", &proxy2);
QQmlComponent component(window->engine());
- component.setData("import QtQuick 2.4; ListView { "
- "anchors.fill: parent; model: _model; delegate: Text { width: parent.width;"
+ component.setData("import QtQuick 2.4; ListView { id: listView; "
+ "anchors.fill: parent; model: _model; delegate: Text { width: listView.width;"
"text: model.display; } }",
QUrl());
window->setContent(QUrl(), &component, component.create());
@@ -9802,12 +9820,24 @@ void tst_QQuickListView::delegateWithRequiredProperties()
void tst_QQuickListView::reuse_reuseIsOffByDefault()
{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
// Check that delegate recycling is off by default. The reason is that
// ListView needs to be backwards compatible with legacy applications. And
// when using delegate recycling, there are certain differences, like that
// a delegates Component.onCompleted will just be called the first time the
// item is created, and not when it's reused.
QScopedPointer<QQuickView> window(createView());
+
+ QaimModel model;
+ for (int i = 0; i < 40; i++)
+ model.addItem("Item" + QString::number(i), "");
+
+ QQmlContext *ctxt = window->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+
+ ctxt->setContextProperty("testObject", testObject.data());
+
window->setSource(testFileUrl("listviewtest.qml"));
window->resize(640, 480);
window->show();
diff --git a/tests/auto/quick/qquicklistview2/BLACKLIST b/tests/auto/quick/qquicklistview2/BLACKLIST
new file mode 100644
index 0000000000..b61ef889c0
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/BLACKLIST
@@ -0,0 +1,3 @@
+[tapDelegateDuringFlicking]
+android
+
diff --git a/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml b/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml
new file mode 100644
index 0000000000..a40ba1cd7e
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/buttonDelegate.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+ListView {
+ id: root
+ width: 320
+ height: 480
+ model: 100
+
+ property var pressedDelegates: []
+ property var releasedDelegates: []
+ property var tappedDelegates: []
+ property var canceledDelegates: []
+
+ delegate: Button {
+ required property int index
+ objectName: text
+ text: "button " + index
+ height: 100
+ width: 320
+
+ onPressed: root.pressedDelegates.push(index)
+ onReleased: root.releasedDelegates.push(index)
+ onClicked: root.tappedDelegates.push(index)
+ onCanceled: root.canceledDelegates.push(index)
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml b/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml
new file mode 100644
index 0000000000..ad556913a5
--- /dev/null
+++ b/tests/auto/quick/qquicklistview2/data/mouseAreaDelegate.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.15
+
+ListView {
+ id: root
+ width: 320
+ height: 480
+ model: 100
+
+ property var pressedDelegates: []
+ property var releasedDelegates: []
+ property var tappedDelegates: []
+ property var canceledDelegates: []
+
+ delegate: MouseArea {
+ height: 100
+ width: 320
+
+ onPressed: root.pressedDelegates.push(index)
+ onReleased: root.releasedDelegates.push(index)
+ onClicked: root.tappedDelegates.push(index)
+ onCanceled: root.canceledDelegates.push(index)
+
+ Rectangle {
+ id: buttonArea
+ anchors.fill: parent
+ border.color: "#41cd52"
+ color: parent.pressed ? "lightsteelblue" : "beige"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
index 27bf389891..7749e83b47 100644
--- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
+++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp
@@ -38,6 +38,8 @@
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
using namespace QQuickViewTestUtils;
using namespace QQuickVisualTestUtils;
@@ -60,6 +62,12 @@ private slots:
void sectionsNoOverlap();
void metaSequenceAsModel();
void noCrashOnIndexChange();
+ void tapDelegateDuringFlicking_data();
+ void tapDelegateDuringFlicking();
+
+private:
+ void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
+ QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
};
tst_QQuickListView2::tst_QQuickListView2()
@@ -307,6 +315,85 @@ void tst_QQuickListView2::noCrashOnIndexChange()
QCOMPARE(items->property("count").toInt(), 4);
}
+void tst_QQuickListView2::tapDelegateDuringFlicking_data()
+{
+ QTest::addColumn<QByteArray>("qmlFile");
+ QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior");
+
+ QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds);
+ QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds);
+ QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds);
+ QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+ QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml")
+ << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
+}
+
+void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832
+{
+ QFETCH(QByteArray, qmlFile);
+ QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
+
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData())));
+ QQuickListView *listView = qobject_cast<QQuickListView*>(window.rootObject());
+ QVERIFY(listView);
+ listView->setBoundsBehavior(boundsBehavior);
+
+ flickWithTouch(&window, {100, 400}, {100, 100});
+ QTRY_VERIFY(listView->contentY() > 501); // let it flick some distance
+ QVERIFY(listView->isFlicking()); // we want to test the case when it's still moving while we tap
+ // @y = 400 we pressed the 4th delegate; started flicking, and the press was canceled
+ QCOMPARE(listView->property("pressedDelegates").toList().first(), 4);
+ QCOMPARE(listView->property("canceledDelegates").toList().first(), 4);
+
+ // press a delegate during flicking (at y > 501 + 100, so likely delegate 6)
+ QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 100});
+ QQuickTouchUtils::flush(&window);
+ QTest::touchEvent(&window, touchDevice.data()).release(0, {100, 100});
+ QQuickTouchUtils::flush(&window);
+
+ const QVariantList pressedDelegates = listView->property("pressedDelegates").toList();
+ const QVariantList releasedDelegates = listView->property("releasedDelegates").toList();
+ const QVariantList tappedDelegates = listView->property("tappedDelegates").toList();
+ const QVariantList canceledDelegates = listView->property("canceledDelegates").toList();
+
+ qCDebug(lcTests) << "pressed" << pressedDelegates; // usually [4, 6]
+ qCDebug(lcTests) << "released" << releasedDelegates;
+ qCDebug(lcTests) << "tapped" << tappedDelegates;
+ qCDebug(lcTests) << "canceled" << canceledDelegates;
+
+ // which delegate received the second press, during flicking?
+ const int lastPressed = pressedDelegates.last().toInt();
+ QVERIFY(lastPressed > 5);
+ QCOMPARE(releasedDelegates.last(), lastPressed);
+ QCOMPARE(tappedDelegates.last(), lastPressed);
+ QCOMPARE(canceledDelegates.count(), 1); // only the first press was canceled, not the second
+}
+
+void tst_QQuickListView2::flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to)
+{
+ QTest::touchEvent(window, touchDevice.data()).press(0, from, window);
+ QQuickTouchUtils::flush(window);
+
+ QPoint diff = to - from;
+ for (int i = 1; i <= 8; ++i) {
+ QTest::touchEvent(window, touchDevice.data()).move(0, from + i * diff / 8, window);
+ QQuickTouchUtils::flush(window);
+ }
+ QTest::touchEvent(window, touchDevice.data()).release(0, to, window);
+ QQuickTouchUtils::flush(window);
+}
+
class SingletonModel : public QStringListModel
{
Q_OBJECT
diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
index c7619ab73c..788323ecf4 100644
--- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
+++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
@@ -32,9 +32,11 @@
#include <QByteArray>
#include <private/qquickshadereffect_p.h>
#include <QMatrix4x4>
-#include <QtQuick/QQuickView>
#include <QtQml/QQmlEngine>
+#include <QtQuick/QQuickView>
+#include <QtQuick/private/qquickshadereffectsource_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQuickTestUtils/private/viewtestutils_p.h>
class TestShaderEffect : public QQuickShaderEffect
{
@@ -120,17 +122,17 @@ void tst_qquickshadereffect::cleanupTestCase()
void tst_qquickshadereffect::testConnection()
{
// verify that the property notify signal is connected
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/connections.qml")));
+ QQuickView view;
+ QVERIFY(QQuickTest::initView(view, QStringLiteral("qrc:/data/connections.qml")));
- auto *shaderEffectItem = qobject_cast<TestShaderEffect*>(view->rootObject());
+ auto *shaderEffectItem = qobject_cast<TestShaderEffect*>(view.rootObject());
QVERIFY(shaderEffectItem);
QCOMPARE(shaderEffectItem->signalsConnected, 0);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
- QSGRendererInterface *rif = view->rendererInterface();
+ QSGRendererInterface *rif = view.rendererInterface();
if (rif && rif->graphicsApi() != QSGRendererInterface::Software)
QCOMPARE(shaderEffectItem->signalsConnected, 1);
}
@@ -138,76 +140,62 @@ void tst_qquickshadereffect::testConnection()
void tst_qquickshadereffect::deleteSourceItem()
{
// purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/deleteSourceItem.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/deleteSourceItem.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
QMetaObject::invokeMethod(obj, "setDeletedSourceItem");
- QTest::qWait(50);
- delete view;
+ const auto shaderEffectSource = obj->findChild<QQuickShaderEffectSource*>();
+ QVERIFY(shaderEffectSource);
+ QTRY_VERIFY(!shaderEffectSource->sourceItem());
}
void tst_qquickshadereffect::deleteShaderEffectSource()
{
- // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/deleteShaderEffectSource.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ // purely to ensure that deleting the ShaderEffectSource doesn't cause a crash
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/deleteShaderEffectSource.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
+ const QPointer<QQuickShaderEffectSource> shaderEffectSource = obj->findChild<QQuickShaderEffectSource*>();
+ QVERIFY(shaderEffectSource);
QMetaObject::invokeMethod(obj, "setDeletedShaderEffectSource");
- QTest::qWait(50);
- delete view;
+ QTRY_VERIFY(shaderEffectSource);
}
void tst_qquickshadereffect::twoImagesOneShaderEffect()
{
// Don't crash when having a ShaderEffect and an Image sharing the texture via supportsAtlasTextures
- QQuickView *view = new QQuickView(nullptr);
- view->setSource(QUrl(QStringLiteral("qrc:/data/twoImagesOneShaderEffect.qml")));
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view));
- QVERIFY(view);
- QObject *obj = view->rootObject();
+ QQuickView view;
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/twoImagesOneShaderEffect.qml")));
+ QObject *obj = view.rootObject();
QVERIFY(obj);
- delete view;
}
void tst_qquickshadereffect::withoutQmlEngine()
{
// using a shader without QML engine used to crash
- auto window = new QQuickWindow;
- auto shaderEffect = new TestShaderEffect(window->contentItem());
+ const QQuickWindow window;
+ auto shaderEffect = new TestShaderEffect(window.contentItem());
shaderEffect->setVertexShader(QUrl());
QVERIFY(shaderEffect->isComponentComplete());
- delete window;
}
// QTBUG-86402: hiding the parent of an item that uses an effect should not cause a crash.
void tst_qquickshadereffect::hideParent()
{
- QScopedPointer<QQuickView> view(new QQuickView);
- view->setSource(QUrl(QStringLiteral("qrc:/data/hideParent.qml")));
- QCOMPARE(view->status(), QQuickView::Ready);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view.data()));
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/data/hideParent.qml")));
+ QVERIFY(QQuickTest::showView(view, QStringLiteral("qrc:/data/hideParent.qml")));
// Should finish without crashing.
- QTRY_VERIFY(view->rootObject()->property("finished").toBool());
+ QTRY_VERIFY(view.rootObject()->property("finished").toBool());
}
void tst_qquickshadereffect::testPropertyMappings()
{
- QScopedPointer<QQuickView> view(new QQuickView);
- view->setSource(QUrl(QStringLiteral("qrc:/data/testProperties.qml")));
- QCOMPARE(view->status(), QQuickView::Ready);
- view->show();
- QVERIFY(QTest::qWaitForWindowExposed(view.data()));
- QTRY_VERIFY(view->rootObject()->property("finished").toBool());
+ QQuickView view;
+ view.setSource(QUrl(QStringLiteral("qrc:/data/testProperties.qml")));
+ QTRY_VERIFY(view.rootObject()->property("finished").toBool());
}
QTEST_MAIN(tst_qquickshadereffect)
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index e7ad2cdada..a4e9c44eab 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -21,7 +21,5 @@ android
# QTBUG-103096
[largeTextObservesViewport]
android
-[imgTagsAlign]
-android
[baselineOffset]
android
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 1f7d000bda..35ff634b2e 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -3278,12 +3278,12 @@ void tst_qquicktext::imgTagsAlign_data()
QTest::addColumn<QString>("src");
QTest::addColumn<int>("imgHeight");
QTest::addColumn<QString>("align");
- QTest::newRow("heart-bottom") << "data/images/heart200.png" << 181 << "bottom";
- QTest::newRow("heart-middle") << "data/images/heart200.png" << 181 << "middle";
- QTest::newRow("heart-top") << "data/images/heart200.png" << 181 << "top";
- QTest::newRow("starfish-bottom") << "data/images/starfish_2.png" << 217 << "bottom";
- QTest::newRow("starfish-middle") << "data/images/starfish_2.png" << 217 << "middle";
- QTest::newRow("starfish-top") << "data/images/starfish_2.png" << 217 << "top";
+ QTest::newRow("heart-bottom") << "images/heart200.png" << 181 << "bottom";
+ QTest::newRow("heart-middle") << "images/heart200.png" << 181 << "middle";
+ QTest::newRow("heart-top") << "images/heart200.png" << 181 << "top";
+ QTest::newRow("starfish-bottom") << "images/starfish_2.png" << 217 << "bottom";
+ QTest::newRow("starfish-middle") << "images/starfish_2.png" << 217 << "middle";
+ QTest::newRow("starfish-top") << "images/starfish_2.png" << 217 << "top";
}
void tst_qquicktext::imgTagsAlign()
@@ -3293,7 +3293,7 @@ void tst_qquicktext::imgTagsAlign()
QFETCH(QString, align);
QString componentStr = "import QtQuick 2.0\nText { text: \"This is a test <img src=\\\"" + src + "\\\" align=\\\"" + align + "\\\"> of image.\" }";
QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("."));
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
@@ -3315,10 +3315,10 @@ void tst_qquicktext::imgTagsAlign()
void tst_qquicktext::imgTagsMultipleImages()
{
- QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.png\\\" width=\\\"60\\\" height=\\\"60\\\" > and another one<img src=\\\"data/images/heart200.png\\\" width=\\\"85\\\" height=\\\"85\\\">.\" }";
+ QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"images/starfish_2.png\\\" width=\\\"60\\\" height=\\\"60\\\" > and another one<img src=\\\"images/heart200.png\\\" width=\\\"85\\\" height=\\\"85\\\">.\" }";
QQmlComponent textComponent(&engine);
- textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("."));
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
@@ -3374,11 +3374,15 @@ void tst_qquicktext::imgTagsUpdates()
void tst_qquicktext::imgTagsError()
{
- QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"data/images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
+ QString componentStr = "import QtQuick 2.0\nText { text: \"This is a starfish<img src=\\\"images/starfish_2.pn\\\" width=\\\"60\\\" height=\\\"60\\\">.\" }";
QQmlComponent textComponent(&engine);
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:2:1: QML Text: Cannot open: file:data/images/starfish_2.pn");
- textComponent.setData(componentStr.toLatin1(), QUrl("file:"));
+ const QString expectedMessage(
+ testFileUrl(".").toString()
+ + ":2:1: QML Text: Cannot open: "
+ + testFileUrl("images/starfish_2.pn").toString());
+ QTest::ignoreMessage(QtWarningMsg, expectedMessage.toLatin1());
+ textComponent.setData(componentStr.toLatin1(), testFileUrl("."));
QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
QVERIFY(textObject != nullptr);
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index 7cdaf6819e..aaf934dc0c 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -2612,8 +2612,6 @@ void tst_qquicktextedit::linkHover()
QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
QCOMPARE(hover.last()[0].toString(), QString());
- texteditObject->setCursor(Qt::OpenHandCursor);
-
QCursor::setPos(linkPos);
QTRY_COMPARE(hover.count(), 3);
QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
@@ -2621,7 +2619,7 @@ void tst_qquicktextedit::linkHover()
QCursor::setPos(textPos);
QTRY_COMPARE(hover.count(), 4);
- QCOMPARE(window.cursor().shape(), Qt::OpenHandCursor);
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
QCOMPARE(hover.last()[0].toString(), QString());
}
#endif
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index fdaaaaca5e..438e6ede02 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1247,6 +1247,7 @@ void tst_qquickwindow::mouseFromTouch_basic()
QCOMPARE(item->lastVelocityFromMouseMove, velocity);
// QVERIFY(item->lastMouseCapabilityFlags.testFlag(QInputDevice::Capability::Velocity)); // TODO
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); // avoid generating a double-click
// Now the same with a transformation.
item->setRotation(90); // clockwise
QMutableEventPoint::setState(points[0], QEventPoint::State::Pressed);
diff --git a/tests/auto/quickcontrols2/controls/data/tst_container.qml b/tests/auto/quickcontrols2/controls/data/tst_container.qml
index be2b9a12ba..5fa3137ade 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_container.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_container.qml
@@ -169,7 +169,7 @@ TestCase {
// don't crash (QTBUG-61310)
function test_repeater(data) {
- var control = createTemporaryObject(data.component)
+ var control = createTemporaryObject(data.component, testCase)
verify(control)
compare(control.itemAt(0).objectName, "0")
@@ -213,4 +213,86 @@ TestCase {
wait(1)
verify(item3)
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ Container {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Container {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ Container {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Container {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_control.qml b/tests/auto/quickcontrols2/controls/data/tst_control.qml
index 4afa719ef6..e12cbbf4ef 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_control.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_control.qml
@@ -1414,4 +1414,86 @@ TestCase {
compare(control.background.width, 100)
compare(control.background.height, 100)
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ Control {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Control {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ Control {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ Control {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
index 7d4c5d9e4d..57216ed11c 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_dialogbuttonbox.qml
@@ -528,4 +528,86 @@ TestCase {
control.destroy()
}
}
+
+ Component {
+ id: contentItemDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ DialogButtonBox {
+ objectName: "control"
+ contentItem: item
+ }
+ }
+ }
+
+ Component {
+ id: contentItemDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ DialogButtonBox {
+ objectName: "control"
+ contentItem: item
+ }
+ Item {
+ id: item
+ objectName: "contentItem"
+ }
+ }
+ }
+
+ function test_contentItemDeletionOrder() {
+ var control1 = createTemporaryObject(contentItemDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(contentItemDeletionOrder2, testCase)
+ verify(control2)
+ }
+
+ Component {
+ id: backgroundDeletionOrder1
+
+ Item {
+ objectName: "parentItem"
+
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ DialogButtonBox {
+ objectName: "control"
+ background: item
+ }
+ }
+ }
+
+ Component {
+ id: backgroundDeletionOrder2
+
+ Item {
+ objectName: "parentItem"
+
+ DialogButtonBox {
+ objectName: "control"
+ background: item
+ }
+ Item {
+ id: item
+ objectName: "backgroundItem"
+ }
+ }
+ }
+
+ function test_backgroundDeletionOrder() {
+ var control1 = createTemporaryObject(backgroundDeletionOrder1, testCase)
+ verify(control1)
+ var control2 = createTemporaryObject(backgroundDeletionOrder2, testCase)
+ verify(control2)
+ }
}
diff --git a/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml b/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
index 437e8ec7f2..210c6594cd 100644
--- a/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
+++ b/tests/auto/quickcontrols2/controls/data/tst_selectionrectangle.qml
@@ -337,7 +337,7 @@ TestCase {
}
// Drag on the bottom right handle, so that the selection shrinks to cell 1, 1
- mouseDrag(tableView, cellWidth * 2, cellHeight * 2, -cellWidth / 2, -cellHeight / 2, Qt.LeftButton)
+ mouseDrag(tableView, (cellWidth * 3) - 1, (cellHeight * 3) - 1, -cellWidth, -cellHeight, Qt.LeftButton)
compare(tableView.selectionModel.selectedIndexes.length, 1)
verify(tableView.selectionModel.isSelected(tableView.model.index(1, 1)))
}
diff --git a/tests/auto/quickcontrols2/cursor/tst_cursor.cpp b/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
index b3a44fa061..452bac15d9 100644
--- a/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
+++ b/tests/auto/quickcontrols2/cursor/tst_cursor.cpp
@@ -58,6 +58,7 @@ private slots:
void editable();
void pageIndicator();
void scrollBar();
+ void textArea();
};
tst_cursor::tst_cursor()
@@ -225,6 +226,19 @@ void tst_cursor::scrollBar()
QCOMPARE(window->cursor().shape(), textArea->cursor().shape());
}
+// QTBUG-104089
+void tst_cursor::textArea()
+{
+ QQuickTextArea textArea;
+ QCOMPARE(textArea.cursor().shape(), Qt::IBeamCursor);
+
+ textArea.setReadOnly(true);
+ QCOMPARE(textArea.cursor().shape(), Qt::ArrowCursor);
+
+ textArea.setSelectByMouse(true);
+ QCOMPARE(textArea.cursor().shape(), Qt::IBeamCursor);
+}
+
QTEST_MAIN(tst_cursor)
#include "tst_cursor.moc"
diff --git a/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml b/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml
new file mode 100644
index 0000000000..0f56ecdd87
--- /dev/null
+++ b/tests/auto/quickcontrols2/qquickmenu/data/customMenuCullItems.qml
@@ -0,0 +1,55 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ width: 200
+ height: 200
+ property alias menu: menu
+
+ Menu {
+ id: menu
+
+ contentItem: FocusScope {
+ implicitHeight: view.implicitHeight
+ Button {
+ anchors {
+ top: parent.top
+ topMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Up"
+ visible: view.interactive
+ }
+ ListView {
+ id: view
+ width: parent.width
+ implicitHeight: Math.min(contentHeight, 300)
+ model: menu.contentModel
+
+ clip: true
+ currentIndex: menu.currentIndex
+ ScrollIndicator.vertical: ScrollIndicator {}
+ }
+ Button {
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+ z: 1
+ text: "Button Down"
+ visible: view.interactive
+ }
+ }
+
+ Repeater {
+ model: 20
+ MenuItem {
+ objectName: "Item: " + modelData
+ text: objectName
+ }
+ }
+ }
+}
diff --git a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
index 9806eff7c3..20db6a47c9 100644
--- a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
+++ b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp
@@ -103,6 +103,7 @@ private slots:
void menuItemWidthAfterImplicitWidthChanged();
void menuItemWidthAfterRetranslate();
void giveMenuItemFocusOnButtonPress();
+ void customMenuCullItems();
};
tst_QQuickMenu::tst_QQuickMenu()
@@ -2000,6 +2001,27 @@ void tst_QQuickMenu::giveMenuItemFocusOnButtonPress()
QTRY_VERIFY(menu->isOpened());
}
+void tst_QQuickMenu::customMenuCullItems()
+{
+ QQuickControlsApplicationHelper helper(this, QLatin1String("customMenuCullItems.qml"));
+ QVERIFY2(helper.ready, helper.failureMessage());
+ QQuickApplicationWindow *window = helper.appWindow;
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
+ QVERIFY(menu);
+ menu->open();
+ QTRY_VERIFY(menu->isOpened());
+
+ QQuickItem *menuItemFirst = menu->itemAt(0);
+ QQuickItem *menuItemLast = menu->itemAt(menu->count() - 1);
+ QVERIFY(menuItemFirst);
+ QVERIFY(menuItemLast);
+ QTRY_VERIFY(!QQuickItemPrivate::get(menuItemFirst)->culled);
+ QTRY_VERIFY(QQuickItemPrivate::get(menuItemLast)->culled);
+}
+
QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu)
#include "tst_qquickmenu.moc"
diff --git a/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
index 1e718ff193..584ce74016 100644
--- a/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
+++ b/tests/auto/quickcontrols2/qquickmenubar/tst_qquickmenubar.cpp
@@ -192,9 +192,10 @@ void tst_qquickmenubar::mouse()
QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu();
QVERIFY(alignmentSubMenu);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint());
-#ifndef Q_OS_ANDROID
+#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
// The screen on Android is too small to fit the whole hierarchy, so the
// Alignment sub-menu is shown on top of View menu.
+ // WebOS also shows alignment sub-menu on top of View menu.
QVERIFY(viewMenuBarMenu->isVisible());
#endif
QVERIFY(alignmentSubMenu->isVisible());
@@ -206,9 +207,10 @@ void tst_qquickmenubar::mouse()
QQuickMenu *verticalSubMenu = verticalSubMenuItem->subMenu();
QVERIFY(verticalSubMenu);
QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint());
-#ifndef Q_OS_ANDROID
+#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
// The screen on Android is too small to fit the whole hierarchy, so the
// Vertical sub-menu is shown on top of View menu and Alignment sub-menu.
+ // WebOS also shows vertical sub-menu on top of View menu and Alignment sub-menu.
QVERIFY(viewMenuBarMenu->isVisible());
QVERIFY(alignmentSubMenu->isVisible());
#endif
@@ -296,6 +298,11 @@ void tst_qquickmenubar::keys()
QVERIFY(editMenuBarItem->isHighlighted());
QVERIFY(editMenuBarItem->hasActiveFocus());
QTRY_VERIFY(!editMenuBarMenu->isVisible());
+
+// There seem to be problems in focus handling in webOS QPA, see https://bugreports.qt.io/browse/WEBOSCI-45
+#ifdef Q_OS_WEBOS
+ QEXPECT_FAIL("", "WEBOSCI-45", Abort);
+#endif
QVERIFY(!cutMenuItem->isHighlighted());
QVERIFY(!cutMenuItem->hasActiveFocus());
@@ -434,8 +441,8 @@ void tst_qquickmenubar::keys()
void tst_qquickmenubar::mnemonics()
{
-#ifdef Q_OS_MACOS
- QSKIP("Mnemonics are not used on macOS");
+#if defined(Q_OS_MACOS) or defined(Q_OS_WEBOS)
+ QSKIP("Mnemonics are not used on this platform");
#endif
QQmlApplicationEngine engine(testFileUrl("menubar.qml"));
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
index 7a298d9620..399f1c67c1 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/applicationwindow.qml
@@ -57,7 +57,9 @@ ApplicationWindow {
property alias popup: popup
property alias popup2: popup2
+ property alias popup3: popup3
property alias button: button
+ property alias slider: slider
Button {
id: button
@@ -79,6 +81,15 @@ ApplicationWindow {
}
}
}
+
+ Popup {
+ id: popup3
+ y: parent.height
+
+ Slider {
+ id: slider
+ }
+ }
}
Popup {
diff --git a/tests/auto/quickcontrols2/qquickpopup/data/window.qml b/tests/auto/quickcontrols2/qquickpopup/data/window.qml
index f6b76b7e14..d46d8ccbf1 100644
--- a/tests/auto/quickcontrols2/qquickpopup/data/window.qml
+++ b/tests/auto/quickcontrols2/qquickpopup/data/window.qml
@@ -58,7 +58,9 @@ Window {
property alias popup: popup
property alias popup2: popup2
+ property alias popup3: popup3
property alias button: button
+ property alias slider: slider
Button {
id: button
@@ -80,6 +82,15 @@ Window {
}
}
}
+
+ Popup {
+ id: popup3
+ y: parent.height
+
+ Slider {
+ id: slider
+ }
+ }
}
Popup {
diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
index 87667989a2..a1412d5ddd 100644
--- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
+++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp
@@ -71,6 +71,8 @@ private slots:
void windowChange();
void closePolicy_data();
void closePolicy();
+ void closePolicy_grabberInside_data();
+ void closePolicy_grabberInside();
void activeFocusOnClose1();
void activeFocusOnClose2();
void activeFocusOnClose3();
@@ -510,12 +512,15 @@ void tst_QQuickPopup::closePolicy()
QVERIFY(popup->isVisible());
QTRY_VERIFY(popup->isOpened());
+ // wait for dimmer
+ QTest::qWait(50);
+
// press outside popup and its parent
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1), 50);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -523,10 +528,10 @@ void tst_QQuickPopup::closePolicy()
// release outside popup and its parent
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside))
+ if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -537,7 +542,7 @@ void tst_QQuickPopup::closePolicy()
if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -548,7 +553,7 @@ void tst_QQuickPopup::closePolicy()
if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent))
QTRY_VERIFY(!popup->isVisible());
else
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
popup->open();
QVERIFY(popup->isVisible());
@@ -557,9 +562,9 @@ void tst_QQuickPopup::closePolicy()
// press inside and release outside
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + popup->x() + 1,
button->y() + popup->y() + 1));
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
- QVERIFY(popup->isVisible());
+ QVERIFY(popup->isOpened());
// escape
QTest::keyClick(window, Qt::Key_Escape);
@@ -569,6 +574,56 @@ void tst_QQuickPopup::closePolicy()
QVERIFY(popup->isVisible());
}
+void tst_QQuickPopup::closePolicy_grabberInside_data()
+{
+ qRegisterMetaType<QQuickPopup::ClosePolicy>();
+
+ QTest::addColumn<QString>("source");
+ QTest::addColumn<QQuickPopup::ClosePolicy>("closePolicy");
+
+ QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside);
+ QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent);
+}
+
+void tst_QQuickPopup::closePolicy_grabberInside()
+{
+ QFETCH(QString, source);
+ QFETCH(QQuickPopup::ClosePolicy, closePolicy);
+
+ QQuickControlsApplicationHelper helper(this, source);
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ QQuickPopup *popup = window->property("popup3").value<QQuickPopup*>();
+ QVERIFY(popup);
+
+ QQuickSlider *slider = window->property("slider").value<QQuickSlider*>();
+ QVERIFY(slider);
+
+ popup->setModal(true);
+ popup->setFocus(true);
+ popup->setClosePolicy(closePolicy);
+
+ popup->open();
+ QVERIFY(popup->isVisible());
+ QTRY_VERIFY(popup->isOpened());
+
+ // press on a mouse grabber inside and release outside
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier,
+ slider->handle()->mapToItem(window->contentItem(),slider->handle()->boundingRect().center()).toPoint());
+
+ QVERIFY(popup->isOpened());
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
+ QVERIFY(popup->isOpened());
+}
+
void tst_QQuickPopup::activeFocusOnClose1()
{
// Test that a popup that never sets focus: true (e.g. ToolTip) doesn't affect
diff --git a/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp b/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
index 1408bafb43..c1e7505819 100644
--- a/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
+++ b/tests/auto/quickcontrols2/qquickstyle/tst_qquickstyle.cpp
@@ -144,7 +144,9 @@ void tst_QQuickStyle::configurationFile()
// Make it small so that there's less possibility for the default/system
// pixel size to match it and give us false positives.
QCOMPARE(label->font().pixelSize(), 3);
+#ifdef QT_BUILD_INTERNAL
QCOMPARE(QQuickLabelPrivate::get(label)->palette()->windowText(), Qt::red);
+#endif
}
void tst_QQuickStyle::commandLineArgument()
diff --git a/tests/auto/quickcontrols2/snippets/CMakeLists.txt b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
index ce76c4fbc5..aaae0ae783 100644
--- a/tests/auto/quickcontrols2/snippets/CMakeLists.txt
+++ b/tests/auto/quickcontrols2/snippets/CMakeLists.txt
@@ -10,11 +10,27 @@ file(GLOB_RECURSE test_data_glob
${CMAKE_CURRENT_SOURCE_DIR}/data/*)
list(APPEND test_data ${test_data_glob})
+set(SNIPPETS_PATH \\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets\\\")
+
+if(WEBOS)
+ # Collect snippets for webOS
+ file(GLOB_RECURSE test_snippets_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets/*)
+ list(APPEND test_snippets ${test_snippets_glob})
+
+ # Copy snippets to a location which is included in the webOS emulator image
+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/snippets)
+ file(COPY ${test_snippets} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/data/snippets)
+
+ set(SNIPPETS_PATH \\\"./data/snippets\\\")
+endif()
+
qt_internal_add_test(tst_snippets
SOURCES
tst_snippets.cpp
DEFINES
- QQC2_SNIPPETS_PATH=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2/doc/snippets\\\"
+ QQC2_SNIPPETS_PATH=${SNIPPETS_PATH}
PUBLIC_LIBRARIES
Qt::Gui
Qt::Quick
diff --git a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
index 010924e378..5697ae50e2 100644
--- a/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
+++ b/tests/auto/quickcontrols2/styleimportscompiletimeqmlonly/tst_styleimportscompiletimeqmlonly.cpp
@@ -60,7 +60,7 @@ tst_StyleImportsCompileTimeQmlOnly::tst_StyleImportsCompileTimeQmlOnly()
void tst_StyleImportsCompileTimeQmlOnly::importQmlOnlyStyleWithoutControls()
{
QQuickControlsApplicationHelper helper(this,
- QLatin1String("importQmlOnlyStyleWithoutControls.qml"), QStringList() << dataDirectory());
+ QLatin1String("importQmlOnlyStyleWithoutControls.qml"), {}, QStringList() << dataDirectory());
QVERIFY2(helper.ready, helper.failureMessage());
auto button = helper.window->property("button").value<QQuickButton*>();
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
index bcf39c598d..822b92dd19 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/BLACKLIST
@@ -5,6 +5,3 @@
# QTBUG-92585
[fileMode:OpenFiles]
*
-#QTBUG-101329
-[goUp]
-qnx
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
new file mode 100644
index 0000000000..b7ff588b68
--- /dev/null
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/data/setSelectedFile.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: root
+ width: 640
+ height: 480
+
+ required property url tempFile1Url
+ required property int fileMode
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ fileMode: root.fileMode
+ selectedFile: root.tempFile1Url
+ }
+}
diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
index e70c7e7171..9cc58377b3 100644
--- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
+++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -69,6 +69,7 @@ public:
private slots:
void initTestCase() override;
+ void init();
void cleanupTestCase();
void defaults();
@@ -79,6 +80,7 @@ private slots:
void bindCurrentFolder_data();
void bindCurrentFolder();
void changeFolderViaStandardButtons();
+ void changeFolderViaDoubleClick_data();
void changeFolderViaDoubleClick();
void chooseFolderViaTextEdit();
void chooseFolderViaEnter();
@@ -101,6 +103,8 @@ private slots:
void defaultSuffix();
void done_data();
void done();
+ void setSelectedFile_data();
+ void setSelectedFile();
private:
QTemporaryDir tempDir;
@@ -122,7 +126,7 @@ void tst_QQuickFileDialogImpl::initTestCase()
{
QQmlDataTest::initTestCase();
- qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", "1");
+ qputenv("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE", "1");
QVERIFY(tempDir.isValid());
// QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
@@ -172,6 +176,12 @@ void tst_QQuickFileDialogImpl::initTestCase()
QDir::setCurrent(tempDir.path());
}
+void tst_QQuickFileDialogImpl::init()
+{
+ // Do this before each test function in case the test sets it.
+ qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", "1");
+}
+
void tst_QQuickFileDialogImpl::cleanupTestCase()
{
// Just in case...
@@ -323,6 +333,7 @@ void tst_QQuickFileDialogImpl::chooseFileViaEnter()
COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
QVERIFY(fileDialogListView);
+ QCOMPARE(fileDialogListView->currentIndex(), 0);
QQuickFileDialogDelegate *subDirDelegate = nullptr;
QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
@@ -428,8 +439,20 @@ void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
}
+void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick_data()
+{
+ QTest::addColumn<bool>("showDirsFirst");
+
+ QTest::newRow("showDirsFirst=true") << true;
+ QTest::newRow("showDirsFirst=false") << false;
+}
+
void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
{
+ QFETCH(bool, showDirsFirst);
+
+ qputenv("QT_QUICK_DIALOGS_SHOW_DIRS_FIRST", showDirsFirst ? "true" : "false");
+
// Open the dialog.
DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
@@ -441,20 +464,20 @@ void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
QVERIFY(fileDialogListView);
QQuickFileDialogDelegate *subDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ const int subDirIndex = showDirsFirst ? 0 : 2;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, subDirIndex, subDirDelegate));
COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
QVERIFY(doubleClickButton(subDirDelegate));
- // The first file in the directory should be selected, which is "sub-sub-dir".
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
+ // The first file in the directory should now be selected.
+ const QUrl firstFileUrl = showDirsFirst ? QUrl::fromLocalFile(tempSubSubDir.path()) : QUrl::fromLocalFile(tempSubFile1->fileName());
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), firstFileUrl);
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { firstFileUrl });
COMPARE_URL(dialogHelper.dialog->currentFile(), dialogHelper.dialog->selectedFile());
COMPARE_URLS(dialogHelper.dialog->currentFiles(), { dialogHelper.dialog->selectedFiles() });
- QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
- QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
- QCOMPARE(subSubDirDelegate->isHighlighted(), true);
- COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
- COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ QQuickFileDialogDelegate *firstDelegateInSubDir = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, firstDelegateInSubDir));
+ QCOMPARE(firstDelegateInSubDir->isHighlighted(), true);
// Since we only chose a folder, the dialog should still be open.
QVERIFY(dialogHelper.dialog->isVisible());
@@ -669,7 +692,7 @@ void tst_QQuickFileDialogImpl::goUp()
QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
QCOMPARE(subDirDelegate->isHighlighted(), true);
- // Go up a directory via the keyboard shortcut next to the breadcrumb bar.
+ // Go up a directory via the keyboard shortcut.
const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
QTest::keySequence(dialogHelper.window(), goUpKeySequence);
QDir tempParentDir(tempDir.path());
@@ -1313,6 +1336,76 @@ void tst_QQuickFileDialogImpl::done()
QCOMPARE(dialogHelper.dialog->result(), result);
}
+void tst_QQuickFileDialogImpl::setSelectedFile_data()
+{
+ fileMode_data();
+}
+
+void tst_QQuickFileDialogImpl::setSelectedFile()
+{
+ QFETCH(QQuickFileDialog::FileMode, fileMode);
+
+ // Open the dialog.
+ const QVariantMap initialProperties = {
+ { "tempFile1Url", QVariant::fromValue(QUrl::fromLocalFile(tempFile1->fileName())) },
+ { "fileMode", QVariant::fromValue(fileMode) }
+ };
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(
+ this, "setSelectedFile.qml", {}, initialProperties);
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // The selected file should be what we set.
+ const auto tempFile1Url = QUrl::fromLocalFile(tempFile1->fileName());
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile1Url);
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), tempFile1Url);
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ // Check that the relevant delegate in the view is current and has focus.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *tempFile1Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 1, tempFile1Delegate));
+ COMPARE_URL(tempFile1Delegate->file(), tempFile1Url);
+ QCOMPARE(fileDialogListView->currentIndex(), 1);
+ QVERIFY(tempFile1Delegate->hasActiveFocus());
+
+ // Next, we select a different file via the UI, close the dialog, set selectedFile again and check that it's still respected.
+
+ // Select the first file in the view by navigating with the down key.
+ QCOMPARE(dialogHelper.window()->activeFocusItem(), tempFile1Delegate);
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
+ const auto tempFile2Url = QUrl::fromLocalFile(tempFile2->fileName());
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), tempFile2Url);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
+
+ // Select the delegate by pressing enter.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { tempFile2Url });
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QCOMPARE(dialogHelper.dialog->result(), QQuickFileDialog::Accepted);
+
+ // Set a different initial selectedFile and re-open.
+ dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile2->fileName()));
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ // TODO: move this duplicated code into a helper
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), tempFile2Url);
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), tempFile2Url);
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ // Check that the relevant delegate in the view is current and has focus.
+ QQuickFileDialogDelegate *tempFile2Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, tempFile2Delegate));
+ COMPARE_URL(tempFile2Delegate->file(), tempFile2Url);
+ QCOMPARE(fileDialogListView->currentIndex(), 2);
+ QVERIFY(tempFile2Delegate->hasActiveFocus());
+}
+
QTEST_MAIN(tst_QQuickFileDialogImpl)
#include "tst_qquickfiledialogimpl.moc"
diff --git a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
index 211725b8ce..d0eae9b07b 100644
--- a/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
+++ b/tests/manual/quickdialogs/dialogs/FileDialogPage.qml
@@ -273,7 +273,6 @@ ColumnLayout {
TextField {
id: selectedFileTextField
text: fileDialog.selectedFile
- readOnly: true
selectByMouse: true
Layout.fillWidth: true
@@ -352,6 +351,7 @@ ColumnLayout {
| hideNameFilterDetailsCheckBox.fileOption
nameFilters: nameFiltersTextField.text.split(",")
rejectLabel: rejectLabelTextField.text
+ selectedFile: selectedFileTextField.text
}
}
}
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 48545ff2d2..2e7a5273b6 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -6,7 +6,8 @@ if(QT_FEATURE_qml_devtools)
add_subdirectory(qmlimportscanner)
add_subdirectory(qmlformat)
add_subdirectory(qmltc)
- if (TARGET Qt::LanguageServerPrivate)
+ if (TARGET Qt::LanguageServerPrivate
+ AND NOT WASM AND NOT IOS AND NOT ANDROID AND NOT QNX AND NOT INTEGRITY AND NOT WEBOS)
add_subdirectory(qmlls)
endif()
endif()
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 605ea03400..e3b195ae5c 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -34,10 +34,13 @@
#include <private/qqmljsresourcefilemapper_p.h>
#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
+#include <QtCore/QHash>
#include <QtCore/QSet>
#include <QtCore/QStringList>
#include <QtCore/QMetaObject>
@@ -47,12 +50,20 @@
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QLibraryInfo>
+#include <QtCore/QLoggingCategory>
#include <iostream>
#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
QT_USE_NAMESPACE
+Q_LOGGING_CATEGORY(lcImportScanner, "qt.qml.import.scanner");
+Q_LOGGING_CATEGORY(lcImportScannerFiles, "qt.qml.import.scanner.files");
+
+using FileImportsWithoutDepsCache = QHash<QString, QVariantList>;
+
namespace {
QStringList g_qmlImportPaths;
@@ -143,8 +154,9 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
return imports;
}
-QVariantList findQmlImportsInQmlFile(const QString &filePath);
-QVariantList findQmlImportsInJavascriptFile(const QString &filePath);
+QVariantList findQmlImportsInFileWithoutDeps(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache);
static QString versionSuffix(QTypeRevision version)
{
@@ -154,7 +166,18 @@ static QString versionSuffix(QTypeRevision version)
// Read the qmldir file, extract a list of plugins by
// parsing the "plugin", "import", and "classname" directives.
-QVariantMap pluginsForModulePath(const QString &modulePath, const QString &version) {
+QVariantMap pluginsForModulePath(const QString &modulePath,
+ const QString &version,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache) {
+ using Cache = QHash<QPair<QString, QString>, QVariantMap>;
+ static Cache pluginsCache;
+ const QPair<QString, QString> cacheKey = std::make_pair(modulePath, version);
+ const Cache::const_iterator it = pluginsCache.find(cacheKey);
+ if (it != pluginsCache.end()) {
+ return *it;
+ }
+
QFile qmldirFile(modulePath + QLatin1String("/qmldir"));
if (!qmldirFile.exists()) {
qWarning() << "qmldir file not found at" << modulePath;
@@ -227,14 +250,16 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi
const QString componentFullPath = modulePath + QLatin1Char('/') + component.fileName;
componentFiles.append(componentFullPath);
importsFromFiles
- += findQmlImportsInQmlFile(componentFullPath);
+ += findQmlImportsInFileWithoutDeps(componentFullPath,
+ fileImportsWithoutDepsCache);
}
const auto scripts = parser.scripts();
for (const auto &script : scripts) {
const QString scriptFullPath = modulePath + QLatin1Char('/') + script.fileName;
scriptFiles.append(scriptFullPath);
importsFromFiles
- += findQmlImportsInJavascriptFile(scriptFullPath);
+ += findQmlImportsInFileWithoutDeps(scriptFullPath,
+ fileImportsWithoutDepsCache);
}
for (const QVariant &import : importsFromFiles) {
@@ -247,16 +272,23 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi
version.isEmpty() ? name : (name + QLatin1Char(' ') + version));
}
- if (!importsAndDependencies.isEmpty())
+ if (!importsAndDependencies.isEmpty()) {
+ importsAndDependencies.removeDuplicates();
pluginInfo[dependenciesLiteral()] = importsAndDependencies;
- if (!componentFiles.isEmpty())
+ }
+ if (!componentFiles.isEmpty()) {
+ componentFiles.sort();
pluginInfo[componentsLiteral()] = componentFiles;
- if (!scriptFiles.isEmpty())
+ }
+ if (!scriptFiles.isEmpty()) {
+ scriptFiles.sort();
pluginInfo[scriptsLiteral()] = scriptFiles;
+ }
if (!parser.preferredPath().isEmpty())
pluginInfo[preferLiteral()] = parser.preferredPath();
+ pluginsCache.insert(cacheKey, pluginInfo);
return pluginInfo;
}
@@ -326,75 +358,198 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
return candidate;
}
-// Find absolute file system paths and plugins for a list of modules.
-QVariantList findPathsForModuleImports(const QVariantList &imports)
+// Provides a hasher for module details stored in a QVariantMap disguised as a QVariant..
+// Only supports a subset of types.
+struct ImportVariantHasher {
+ std::size_t operator()(const QVariant &importVariant) const
+ {
+ size_t computedHash = 0;
+ QVariantMap importMap = qvariant_cast<QVariantMap>(importVariant);
+ for (auto it = importMap.constKeyValueBegin(); it != importMap.constKeyValueEnd(); ++it) {
+ const QString &key = it->first;
+ const QVariant &value = it->second;
+
+ if (!value.isValid() || value.isNull()) {
+ computedHash = qHashMulti(computedHash, key, 0);
+ continue;
+ }
+
+ const auto valueTypeId = value.typeId();
+ switch (valueTypeId) {
+ case QMetaType::QString:
+ computedHash = qHashMulti(computedHash, key, value.toString());
+ break;
+ case QMetaType::Bool:
+ computedHash = qHashMulti(computedHash, key, value.toBool());
+ break;
+ case QMetaType::QStringList:
+ computedHash = qHashMulti(computedHash, key, value.toStringList());
+ break;
+ default:
+ Q_ASSERT_X(valueTypeId, "ImportVariantHasher", "Invalid variant type detected");
+ break;
+ }
+ }
+
+ return computedHash;
+ }
+};
+
+using ImportDetailsAndDeps = QPair<QVariantMap, QStringList>;
+
+// Returns the import information as it will be written out to the json / .cmake file.
+// The dependencies are not stored in the same QVariantMap because we don't currently need that
+// information in the output file.
+ImportDetailsAndDeps
+getImportDetails(const QVariant &inputImport,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache) {
+
+ using Cache = std::unordered_map<QVariant, ImportDetailsAndDeps, ImportVariantHasher>;
+ static Cache cache;
+
+ const Cache::const_iterator it = cache.find(inputImport);
+ if (it != cache.end()) {
+ return it->second;
+ }
+
+ QVariantMap import = qvariant_cast<QVariantMap>(inputImport);
+ QStringList dependencies;
+ if (import.value(typeLiteral()) == moduleLiteral()) {
+ const QString version = import.value(versionLiteral()).toString();
+ const QPair<QString, QString> paths =
+ resolveImportPath(import.value(nameLiteral()).toString(), version);
+ QVariantMap plugininfo;
+ if (!paths.first.isEmpty()) {
+ import.insert(pathLiteral(), paths.first);
+ import.insert(relativePathLiteral(), paths.second);
+ plugininfo = pluginsForModulePath(paths.first,
+ version,
+ fileImportsWithoutDepsCache);
+ }
+ QString linkTarget = plugininfo.value(linkTargetLiteral()).toString();
+ QString plugins = plugininfo.value(pluginsLiteral()).toString();
+ bool isOptional = plugininfo.value(pluginIsOptionalLiteral(), QVariant(false)).toBool();
+ QString classnames = plugininfo.value(classnamesLiteral()).toString();
+ QStringList components = plugininfo.value(componentsLiteral()).toStringList();
+ QStringList scripts = plugininfo.value(scriptsLiteral()).toStringList();
+ QString prefer = plugininfo.value(preferLiteral()).toString();
+ if (!linkTarget.isEmpty())
+ import.insert(linkTargetLiteral(), linkTarget);
+ if (!plugins.isEmpty())
+ import.insert(QStringLiteral("plugin"), plugins);
+ if (isOptional)
+ import.insert(pluginIsOptionalLiteral(), true);
+ if (!classnames.isEmpty())
+ import.insert(QStringLiteral("classname"), classnames);
+ if (plugininfo.contains(dependenciesLiteral())) {
+ dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
+ }
+ if (!components.isEmpty()) {
+ components.removeDuplicates();
+ import.insert(componentsLiteral(), components);
+ }
+ if (!scripts.isEmpty()) {
+ scripts.removeDuplicates();
+ import.insert(scriptsLiteral(), scripts);
+ }
+ if (!prefer.isEmpty()) {
+ import.insert(preferLiteral(), prefer);
+ }
+ }
+ import.remove(versionLiteral());
+
+ const ImportDetailsAndDeps result = {import, dependencies};
+ cache.insert({inputImport, result});
+ return result;
+}
+
+// Parse a dependency string line into a QVariantMap, to be used as a key when processing imports
+// in getGetDetailedModuleImportsIncludingDependencies.
+QVariantMap dependencyStringToImport(const QString &line) {
+ const auto dep = QStringView{line}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ const QString name = dep[0].toString();
+ QVariantMap depImport;
+ depImport[typeLiteral()] = moduleLiteral();
+ depImport[nameLiteral()] = name;
+ if (dep.length() > 1)
+ depImport[versionLiteral()] = dep[1].toString();
+ return depImport;
+}
+
+// Returns details of given input import and its recursive module dependencies.
+// The details include absolute file system paths for the the module plugin, components,
+// etc.
+// An internal cache is used to prevent repeated computation for the same input module.
+QVariantList getGetDetailedModuleImportsIncludingDependencies(
+ const QVariant &inputImport,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache)
{
+ using Cache = std::unordered_map<QVariant, QVariantList, ImportVariantHasher>;
+ static Cache importsCacheWithDeps;
+
+ const Cache::const_iterator it = importsCacheWithDeps.find(inputImport);
+ if (it != importsCacheWithDeps.end()) {
+ return it->second;
+ }
+
QVariantList done;
- QVariantList importsCopy(imports);
-
- for (int i = 0; i < importsCopy.length(); ++i) {
- QVariantMap import = qvariant_cast<QVariantMap>(importsCopy.at(i));
- if (import.value(typeLiteral()) == moduleLiteral()) {
- const QString version = import.value(versionLiteral()).toString();
- const QPair<QString, QString> paths =
- resolveImportPath(import.value(nameLiteral()).toString(), version);
- QVariantMap plugininfo;
- if (!paths.first.isEmpty()) {
- import.insert(pathLiteral(), paths.first);
- import.insert(relativePathLiteral(), paths.second);
- plugininfo = pluginsForModulePath(paths.first, version);
- }
- QString linkTarget = plugininfo.value(linkTargetLiteral()).toString();
- QString plugins = plugininfo.value(pluginsLiteral()).toString();
- bool isOptional = plugininfo.value(pluginIsOptionalLiteral(), QVariant(false)).toBool();
- QString classnames = plugininfo.value(classnamesLiteral()).toString();
- QStringList components = plugininfo.value(componentsLiteral()).toStringList();
- QStringList scripts = plugininfo.value(scriptsLiteral()).toStringList();
- QString prefer = plugininfo.value(preferLiteral()).toString();
- if (!linkTarget.isEmpty())
- import.insert(linkTargetLiteral(), linkTarget);
- if (!plugins.isEmpty())
- import.insert(QStringLiteral("plugin"), plugins);
- if (isOptional)
- import.insert(pluginIsOptionalLiteral(), true);
- if (!classnames.isEmpty())
- import.insert(QStringLiteral("classname"), classnames);
- if (plugininfo.contains(dependenciesLiteral())) {
- const QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
- for (const QString &line : dependencies) {
- const auto dep = QStringView{line}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
- const QString name = dep[0].toString();
- QVariantMap depImport;
- depImport[typeLiteral()] = moduleLiteral();
- depImport[nameLiteral()] = name;
- if (dep.length() > 1)
- depImport[versionLiteral()] = dep[1].toString();
-
- if (!importsCopy.contains(depImport))
- importsCopy.append(depImport);
+ QVariantList importsToProcess;
+ std::unordered_set<QVariant, ImportVariantHasher> importsSeen;
+ importsToProcess.append(inputImport);
+
+ for (int i = 0; i < importsToProcess.length(); ++i) {
+ const QVariant importToProcess = importsToProcess.at(i);
+ auto [details, deps] = getImportDetails(importToProcess, fileImportsWithoutDepsCache);
+ if (details.value(typeLiteral()) == moduleLiteral()) {
+ for (const QString &line : deps) {
+ const QVariantMap depImport = dependencyStringToImport(line);
+
+ // Skip self-dependencies.
+ if (depImport == importToProcess)
+ continue;
+
+ if (importsSeen.find(depImport) == importsSeen.end()) {
+ importsToProcess.append(depImport);
+ importsSeen.insert(depImport);
}
}
- if (!components.isEmpty()) {
- components.removeDuplicates();
- import.insert(componentsLiteral(), components);
- }
- if (!scripts.isEmpty()) {
- scripts.removeDuplicates();
- import.insert(scriptsLiteral(), scripts);
- }
- if (!prefer.isEmpty()) {
- import.insert(preferLiteral(), prefer);
- }
}
- import.remove(versionLiteral());
- done.append(import);
+ done.append(details);
}
+
+ importsCacheWithDeps.insert({inputImport, done});
return done;
}
+QVariantList mergeImports(const QVariantList &a, const QVariantList &b);
+
+// Returns details of given input imports and their recursive module dependencies.
+QVariantList getGetDetailedModuleImportsIncludingDependencies(
+ const QVariantList &inputImports,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache)
+{
+ QVariantList result;
+
+ // Get rid of duplicates in input module list.
+ QVariantList inputImportsCopy;
+ inputImportsCopy = mergeImports(inputImportsCopy, inputImports);
+
+ // Collect recursive dependencies for each input module and merge into result, discarding
+ // duplicates.
+ for (auto it = inputImportsCopy.begin(); it != inputImportsCopy.end(); ++it) {
+ QVariantList imports = getGetDetailedModuleImportsIncludingDependencies(
+ *it, fileImportsWithoutDepsCache);
+ result = mergeImports(result, imports);
+ }
+ return result;
+}
+
// Scan a single qml file for import statements
QVariantList findQmlImportsInQmlCode(const QString &filePath, const QString &code)
{
+ qCDebug(lcImportScannerFiles) << "Parsing code and finding imports in" << filePath
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+
QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine);
lexer.setCode(code, /*line = */ 1);
@@ -490,9 +645,17 @@ QVariantList findQmlImportsInJavascriptFile(const QString &filePath)
return collector.imports;
}
-// Scan a single qml or js file for import statements
-QVariantList findQmlImportsInFile(const QString &filePath)
+// Scan a single qml or js file for import statements without resolving dependencies.
+QVariantList findQmlImportsInFileWithoutDeps(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
+ const FileImportsWithoutDepsCache::const_iterator it =
+ fileImportsWithoutDepsCache.find(filePath);
+ if (it != fileImportsWithoutDepsCache.end()) {
+ return *it;
+ }
+
QVariantList imports;
if (filePath == QLatin1String("-")) {
QFile f;
@@ -502,12 +665,47 @@ QVariantList findQmlImportsInFile(const QString &filePath)
imports = findQmlImportsInQmlFile(filePath);
} else if (filePath.endsWith(QLatin1String(".js"))) {
imports = findQmlImportsInJavascriptFile(filePath);
+ } else {
+ qCDebug(lcImportScanner) << "Skipping file because it's not a .qml/.js file";
+ return imports;
}
- return findPathsForModuleImports(imports);
+ fileImportsWithoutDepsCache.insert(filePath, imports);
+ return imports;
+}
+
+// Scan a single qml or js file for import statements, resolve dependencies and return the full
+// list of modules the file depends on.
+QVariantList findQmlImportsInFile(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache) {
+ const auto fileProcessTimeBegin = QDateTime::currentDateTime();
+
+ QVariantList imports = findQmlImportsInFileWithoutDeps(filePath,
+ fileImportsWithoutDepsCache);
+ if (imports.empty())
+ return imports;
+
+ const auto pathsTimeBegin = QDateTime::currentDateTime();
+
+ qCDebug(lcImportScanner) << "Finding module paths for imported modules in" << filePath
+ << "TS:" << pathsTimeBegin.toMSecsSinceEpoch();
+ QVariantList importPaths = getGetDetailedModuleImportsIncludingDependencies(
+ imports, fileImportsWithoutDepsCache);
+
+ const auto pathsTimeEnd = QDateTime::currentDateTime();
+ const auto duration = pathsTimeBegin.msecsTo(pathsTimeEnd);
+ const auto fileProcessingDuration = fileProcessTimeBegin.msecsTo(pathsTimeEnd);
+ qCDebug(lcImportScanner) << "Found module paths:" << importPaths.count()
+ << "TS:" << pathsTimeEnd.toMSecsSinceEpoch()
+ << "Path resolution duration:" << duration << "msecs";
+ qCDebug(lcImportScanner) << "Scan duration:" << fileProcessingDuration << "msecs";
+ return importPaths;
}
// Merge two lists of imports, discard duplicates.
+// Empirical tests show that for a small amount of values, the n^2 QVariantList comparison
+// is still faster than using an unordered_set + hashing a complex QVariantMap.
QVariantList mergeImports(const QVariantList &a, const QVariantList &b)
{
QVariantList merged = a;
@@ -537,7 +735,9 @@ struct pathStartsWith {
// Scan all qml files in directory for import statements
-QVariantList findQmlImportsInDirectory(const QString &qmlDir)
+QVariantList findQmlImportsInDirectory(const QString &qmlDir,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
QVariantList ret;
if (qmlDir.isEmpty())
@@ -571,8 +771,15 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
}
for (const QFileInfo &x : entries)
- if (x.isFile())
- ret = mergeImports(ret, findQmlImportsInFile(x.absoluteFilePath()));
+ if (x.isFile()) {
+ const auto entryAbsolutePath = x.absoluteFilePath();
+ qCDebug(lcImportScanner) << "Scanning file" << entryAbsolutePath
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ ret = mergeImports(ret,
+ findQmlImportsInFile(
+ entryAbsolutePath,
+ fileImportsWithoutDepsCache));
+ }
}
return ret;
}
@@ -580,19 +787,29 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
// Find qml imports recursively from a root set of qml files.
// The directories in qmlDirs are searched recursively.
// The files in qmlFiles parsed directly.
-QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QStringList &scanFiles)
+QVariantList findQmlImportsRecursively(const QStringList &qmlDirs,
+ const QStringList &scanFiles,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
QVariantList ret;
+ qCDebug(lcImportScanner) << "Scanning" << qmlDirs.count() << "root directories and"
+ << scanFiles.count() << "files.";
+
// Scan all app root qml directories for imports
for (const QString &qmlDir : qmlDirs) {
- QVariantList imports = findQmlImportsInDirectory(qmlDir);
+ qCDebug(lcImportScanner) << "Scanning root" << qmlDir
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ QVariantList imports = findQmlImportsInDirectory(qmlDir, fileImportsWithoutDepsCache);
ret = mergeImports(ret, imports);
}
// Scan app qml files for imports
for (const QString &file : scanFiles) {
- QVariantList imports = findQmlImportsInFile(file);
+ qCDebug(lcImportScanner) << "Scanning file" << file
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ QVariantList imports = findQmlImportsInFile(file, fileImportsWithoutDepsCache);
ret = mergeImports(ret, imports);
}
@@ -685,6 +902,9 @@ int main(int argc, char *argv[])
return 1;
}
+ // QQmlDirParser returnes QMultiHashes. Ensure deterministic output.
+ QHashSeed::setDeterministicGlobalSeed();
+
QStringList qmlRootPaths;
QStringList scanFiles;
QStringList qmlImportPaths;
@@ -755,8 +975,13 @@ int main(int argc, char *argv[])
g_qmlImportPaths = qmlImportPaths;
+ FileImportsWithoutDepsCache fileImportsWithoutDepsCache;
+
// Find the imports!
- QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles);
+ QVariantList imports = findQmlImportsRecursively(qmlRootPaths,
+ scanFiles,
+ fileImportsWithoutDepsCache
+ );
QByteArray content;
if (generateCmakeContent) {
diff --git a/tools/qmlls/CMakeLists.txt b/tools/qmlls/CMakeLists.txt
index ac42d239de..85c892f460 100644
--- a/tools/qmlls/CMakeLists.txt
+++ b/tools/qmlls/CMakeLists.txt
@@ -2,10 +2,9 @@
## qmlls Tool:
#####################################################################
-qt_get_tool_target_name(target_name qmlls)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML languageserver"
- TOOLS_TARGET Qml # special case
+qt_internal_add_app(qmlls
+ TARGET_DESCRIPTION "QML Language Server"
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
SOURCES
qlanguageserver.h qlanguageserver_p.h qlanguageserver.cpp
qqmllanguageserver.h qqmllanguageserver.cpp
@@ -19,6 +18,8 @@ qt_internal_add_tool(${target_name}
qqmlcodemodel.h qqmlcodemodel.cpp
../shared/qqmltoolingsettings.h
../shared/qqmltoolingsettings.cpp
+ DEFINES
+ QT_USE_QSTRINGBUILDER
PUBLIC_LIBRARIES
Qt::QmlPrivate
Qt::CorePrivate
@@ -26,4 +27,4 @@ qt_internal_add_tool(${target_name}
Qt::LanguageServerPrivate
Qt::QmlLintPrivate
)
-qt_internal_return_unless_building_tools()
+set_target_properties(qmlls PROPERTIES WIN32_EXECUTABLE FALSE)